ACM模版——约瑟夫问题(出列顺序 + 最后一个)

最后一个:针对 n 范围比较大的解析

0、在公式:ans=(ans+m)%i 的基础上...当i很大,ans 和 m 都比较小的时候(i>>ans 和 m)。ans 每次增加的其实是 m。而这个增加次数可以算出来。所以...

1、当 ans + m >= i 时,普通递推。

2、当 ans + m < i 时,设增加次数为 x 次(代码中为:step 变量)。满足 ans + x * m < i + x - 1。

Ps:之所以是 x - 1,是因为 ans + m < i,ans + m + (x-1) * m < i + x -1。

3、由 2 得:x < (i - ans - 1) / (m - 1);x = ceil((i - ans - 1) / (m - 1)) - 1。

4、由 3 得:ans += x * m,i += x。

5、但如果 i 增加的总次数 (i + x) > n 了,那么最终结果应增加 (n - i + 1) * m 次,而不是 i+x 次。

#include<bits/stdc++.h>
#include<cmath>

#define mem(a,b) memset(a,b,sizeof a)
#define INF 0x3f3f3f3f

using namespace std;

typedef long long ll;

int main()
{
    ll n,m;
    while(~scanf("%lld%lld",&n,&m))
    {
        /* 最后一个:针对n范围比较小的解决方案 */
        // 编号从0开始
//        int ans=0;
//        for(int i=2;i<=n;i++)
//            ans=(ans+m)%i;
//        printf("%d\n",ans); // 输出的是数组下标

        /* 最后一个:针对n范围比较大的解决方案 HDU3089 */
        // 编号从0开始
//        ll ans=0,step=0;
//        if(m==1) ans=n-1;
//        else if(n==1) ans=0;
//        else
//        {
//            for(ll i=2;i<=n;)
//            {
//                if(ans+m<i)
//                {
////                    step=(i-1-ans)%(m-1)?(i-1-ans)/(m-1):(i-1-ans)/(m-1)-1; // 等价下面这行代码
//                    step=ceil((i-1-ans)*1.0/(m-1))-1;
//                    if(i+step>n)
//                    {
//                        ans+=(n-i+1)*m; // (n-i+1)次
//                        break;
//                    }
//
//                    ans+=step*m;
//                    i+=step;
//                }
//                else
//                    ans=(ans+m)%i++;
//            }
//            ans%=n;
//        }
//
//        printf("%lld\n",ans+1); // 输出的是数组下标

        /* 出列顺序:针对出列顺序解决方案O(n*m) */
        // 编号从0开始
//        int idx=0,cnt=1,pout=0,vis[n+10];
//        mem(vis,0);
//        while(pout!=n) // out人数是否满
//        {
//            if(1==vis[idx]) // 已out
//            {
//                idx=(idx+1)%n;
//                continue;
//            }
//
//            if(cnt%m==0) // 准备out
//            {
//                vis[idx]=1;
//                pout++;
//                printf("%d\n",idx+1);
//            }
//
//            idx=(idx+1)%n; // 继续报数
//            cnt++;
//        }

        /* 针对出列顺序解决方案O(n*(logn)^2) */
        // 待更新...
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dream_weave/article/details/80691546