2018沈阳k(经典的约瑟夫环)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Feynman1999/article/details/84175475

地址

K Let the Flames Begin

首先,对于经典的约瑟夫环问题,我们记 f ( n , m ) f(n,m) 表示初始有 n n 个人,第 m m 个出队的人是谁(从0号开始报数)。则有递推式 f ( n , m ) = ( f ( n 1 , m 1 ) + k )   %   n f(n,m)=(f(n-1,m-1)+k)\ \%\ n 其中 k k 表示每报数 k k 次一个人出队,注意编号从0开始

递推式的证明: 考虑现在有 n n 个人围成一圈,然后从0开始报数。假设第一个出队的人是 x x ,这时还有 n 1 n-1 个人,我们从刚刚出去的那个人的下一个人从0重新编号,那么以当前局面重新开始,第 m 1 m-1 个出队的人是初始所求的同一个人,但编号不同,差多少呢?即 f ( n , m ) = ( f ( n 1 , m 1 ) + k )   %   n f(n,m)=(f(n-1,m-1)+k)\ \%\ n 。 +1 -1 细节手玩一下。

回到本题,由于 m , k m,k 可能会很大,但不会同时很大,当 m m 较小的时候( m < = k m<=k ),直接递推即可。

下面考虑 m > k m>k 的情况,会发现模数大部分情况下远大于 k k ,也就是说可以用乘法代替多次加法,这样可以降低时间复杂度。具体代替多少次呢?考虑 f ( a , b ) = a n s f(a,b)=ans , 假设代替 x x 次,则 f ( a + x , b + x ) = a n s + x k f(a+x,b+x) = ans+x*k 进行取模的等价条件是 a n s + x k > = a + x ans+x*k > =a+x ,即 x > = a a n s k 1 x>=\frac{a-ans}{k-1} 即代替次数确定了(整除不整除,快加到 m m 了等细节注意一下即可)。

时间复杂度 O ( ) O(感觉能过)

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=2e6+10;
ll m,n,k;

ll f[maxn];

int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    cin>>t;
    int T=0;
    while(t--)
    {
        T++;
        cin>>n>>m>>k;
        cout<<"Case #"<<T<<": ";
        if(m<=k){//直接递推
            ll tp=(n-m+1);
            f[1] = (k-1) % tp;  //编号从0开始
            for(int i=2;i<=m;++i) f[i] = (f[i-1]+k)%(++tp);
            cout<<f[m]+1<<endl;
        }
        else{
            if(k==1) cout<<m<<endl;
            else{
                ll tp = n-m+1;
                ll ans = (k-1) % tp;  //编号从0开始
                ll now = 1;
                while(1){
                    if((tp-ans)%(k-1)==0){
                        ll x = (tp-ans)/(k-1) -1 ;
                        x = min(x,m-now);
                        ans += x*k;
                        now += x;
                        tp  += x;
                        if(now==m) break;
                        ans =(ans + k) % (tp+1);
                        now +=1;
                        tp  +=1;
                        if(now==m) break;
                        assert(now <=m);
                    }
                    else{
                        ll x = (tp-ans)/(k-1);
                        x = min(x,m-now);
                        ans += x*k;
                        now += x;
                        tp +=x;
                        if(now==m) break;
                        ans =(ans + k) % (tp+1);
                        now +=1;
                        tp  +=1;
                        if(now==m) break;
                        assert(now <=m);
                    }
                }
                cout<<ans+1<<endl;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Feynman1999/article/details/84175475