约瑟夫环以及其变种集合

Posted vikyanite

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了约瑟夫环以及其变种集合相关的知识,希望对你有一定的参考价值。

最近在CF上补题,补到了一道关于约瑟夫环的题目(听都没听过,原谅我太菜)

就去好好学了一下,不过一般的题目应该是不会让你模拟过的,所以这次就做了一个约瑟夫环公式法变形的集合。

关于约瑟夫环的基础讲解,我个人认为最好的就是这篇了。

 

首先是最原始的约瑟夫环的题目:

https://vjudge.net/problem/51Nod-1073(小数据规模)

技术图片
#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n, m;
    int s = 0;
    cin >> n >> m;
    for (int i = 2; i <= n; i++)
        s = (s + m) % i;
    cout << s + 1 << endl;
    return 0;
}
View Code

没啥好讲的直接套公式即可。

https://vjudge.net/problem/51Nod-1074(大数据规模)

技术图片
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    ll n, m;
    ll ans = 0;
    cin >> n >> m;
    for (ll i = 2; i <= n; i++)
    {
        if (ans + m < i)
        {
            ll len = (i - ans) / m;
            if (len + i < n)
            {
                i += len;
                ans += m*len;
            }
            else
            {
                ans += m*(n-i);
                i = n;
            }
        }
        ans = (ans + m) % i;
    }
    cout << ans + 1 << endl;
    return 0;
}
View Code

解题思路

除去超大的n之外。就是个约瑟夫环的裸题。

约瑟夫环递推公式,n为人数,m为步长。

f(1) = 0

f(n) = [f(n-1)+m]%i  i∈[2,n]

// f(n)还要经过起始位置修正,设起始位置为s,即ans=[f(n)+s]%n。

基本约瑟夫环优化就是当k=1的时候,每次递推就是在+1,可以直接算出来快速跳过,f(n)=f(1)+n-1

当n超大的时候,可以照着这种思路快速简化递推过程。在递推后期,f(x)+m在很长的周期内<i,假设有len个周期,

那么这些周期合并后的结果相当于f(x)+len*m。可以快速跳过。条件限制是: f(x)+len*m<i+(len-1)

可以推出来:

当len=1时,条件限制: f(x)+m<i

当len=2是,条件限制: f(x+1)+m<i+1=f(x)+2*m<i+1

当m=m时,条件限制:f(x)+len*m<i+(len-1)

化简有m<(i-f(x)-1)/(m-1),若能整除,就是(i-f(x)-1)/(m-1)-1,否则就是(i-f(x)-1)/(m-1)直接取整。

这样,i+=len,f(x)+=m*len,快速跳过了中间过程。

若i+len>n,说明快速跳越界了,这时候可以直接算出f(n)=f(x)+(n-i-1)*len。

以上是关于约瑟夫环以及其变种集合的主要内容,如果未能解决你的问题,请参考以下文章

ICP算法及其变种

约瑟夫以及其变异体总结

约瑟夫问题及其变形

图-最短路径-Dijkstra及其变种

数据结构--线性表及其应用 约瑟夫环

一个不简洁的约瑟夫环解法