转圈游戏(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就错了

浙公网安备 33010602011771号