转圈游戏(circle)

题意:这个游戏一共有 n 个小朋友参与,每个小朋友一开始会被指定一个编号,保证这个编号恰好构成 1~n 的一个排列,然后站成一圈。每回合,所有小朋友同时摸自己顺时针方向的第一个小朋友的脑袋,如果他 们两人编号的最大公约数不是1,那么被摸脑袋的那个小朋友就在这一回合出局。 显然这个游戏是一定可以在若干回合之后停下来的,因为剩下的小朋友谁也 不能让对方出局了,这些小朋友会共同分享一块蛋糕。 现在,对于一个指定的初始局面,请帮助 HVX 算算,最后有多少小朋友能吃到蛋糕。

对于 50%的测试数据,满足 1≤n≤10^3

对于 80%的测试数据,满足 1≤n≤10^5

对于 100%的测试数据,满足 1≤n≤10^6,m≤10^6,seed 为 int 类型。

方法:1.链表模拟,每次枚举,在链表中O(1)删除,当无法再删除时停下

2.优化枚举范围:用vector存被删的小朋友,依然用链表模拟,但每次从上一轮被删的小朋友向左右枚举,这样减少枚举量

O(n)

code:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1000005;

int n,m,seed;
LL p[maxn],vis[maxn],ans=0;
LL c[maxn],Next[maxn],last[maxn];
vector<int>q[2];

int read(){
    int x=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }
    return x*f;
}

int rd(){
    seed=seed*214013+2531011;
    return (abs(seed))%n+1;
}

int gcd(int x,int y){
    return (y==0?x:gcd(y,x%y));
}

int main(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
    n=read(); m=read(); seed=read();
    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=1;i<=m;i++){
        int x=rd(),y=rd(); swap(p[x],p[y]);
    }
    int loc=0;
    for(int i=1;i<=n;i++){ //找1:1一定不会被删除
        if(p[i]==1){
            loc=i;
            break;
        }
    }
    int cnt=0;                                 //调位置,将1调至开头,方便枚举
    for(int i=1;i<loc;i++) c[++cnt]=p[i];
    for(int i=1;i<=n-loc+1;i++) p[i]=p[i+loc-1];
    for(int i=n-loc+2;i<=n;i++) p[i]=c[i-(n-loc+1)];
    for(int i=1;i<=n;i++){
        Next[i]=i+1; last[i]=i-1;
    }
    Next[n]=1; last[1]=n;
    ans=n;
    for(int i=2;i<n;i++){
        if(gcd(p[i],p[i+1])>1){
            q[0].push_back(i+1);
            vis[i+1]=1;
            ans--;
        }
    }
    for(int i=0;;i++){                 //轮流生成
        if(q[i%2].size()==0) break;
        q[(i+1)%2].clear();
        for(int pos=0;pos<q[i%2].size();pos++){
            int ret=q[i%2][pos];
            Next[last[ret]]=Next[ret];
            last[Next[ret]]=last[ret];
            if(vis[Next[ret]]==0&&gcd(p[Next[ret]],p[last[ret]])!=1){
                ans--;
                q[(i+1)%2].push_back(Next[ret]);
                vis[Next[ret]]=1;
            }
        }
    }
    printf("%lld\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

注:本题中读入方式比较特别,根据参数依照题目意思生成排列

一定要严格按照题目做

比如seed的类型一定是int改成long long就错了

posted @ 2019-11-07 15:17  kmxzc  阅读(966)  评论(0)    收藏  举报