我们发现对于每一个数 p
总会有一个数 \(x \le n\) 使得 \(p\equiv p+x\times d(\mod n)\)
$ \therefore n| x\times d,x=\gcd(n,d)$
当然这个东西对做题的帮助不大,只是告诉我们这个数量是有限的。
那我们就可以把题目看做有很多个环,他们之间互相连边连成了一个大环,对于每一个小环,我们可以用一个并查集来维护,而总的大环,我们亦可以用并查集来维护,对于每一个初始点 \(c_i\),我们可以先找到这个点的最近的没有被全覆盖的小环,在算出到这个小环是的数字是多少,对于这个数字,找到这个数字的下一个在环内的没有被覆盖的数字(不能是 \(s\))。对于最少步数很简单,可以类似模拟的做。
坑点:\(c_i\) 可能大于 \(n\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1E5+5;
int T;
int n,s,q,p,m,d;
int c[N];
int mark[N];
int pos[N],sz[N],nxt[N],fa[N],vis[N],bel[N];
int find1(int x) {return sz[x]?x:nxt[x]=find1(nxt[x]);
}
int find2(int x){return (!vis[x] && x!=s)?x:fa[x]=find2(fa[x]);
}
int ans,tot;
signed main() {scanf("%lld",&T);while(T--) {ans=tot=0;memset(mark,0,sizeof mark);memset(vis,0,sizeof vis);memset(sz,0,sizeof sz);memset(fa,0,sizeof fa);memset(nxt,0,sizeof nxt);memset(bel,0,sizeof bel);memset(pos,0,sizeof pos);scanf("%lld%lld%lld%lld%lld%lld",&n,&s,&q,&p,&m,&d);for(int i=1; i<n; i++)c[i]=(c[i-1]*q+p)%m;for(int i=0; i<n; i++) {if(mark[i])continue;int j=i;while(!mark[j]) {mark[j]=1;sz[i]++;bel[j]=i;if(j==s)sz[i]--;fa[j]=(j+d)%n;j=(j+d)%n;}++tot;}for(int i=0; i<tot; i++)nxt[i]=i+1;nxt[tot-1]=0;//记录下一个有空的环for(int i=1; i<n; i++) {int p=find1(bel[c[i]%n]);sz[p]--;int y=(p-bel[c[i]%n]);if(y<0)y+=tot;//求出最近的空的环pos[i]=find2((c[i]+y)%n);vis[pos[i]]=1;}pos[0]=s;memset(vis,0,sizeof vis);for(int i=0;i<n;i++){if(!vis[i]){vis[i]=1;int now=i;int cnt=1,flag=now==s;while(!vis[pos[now]]){now=pos[now];vis[now]=1;cnt++;if(now==s) flag=1;}if(flag) ans+=cnt-1;else if(cnt>1) ans+=cnt+1;}} cout<<ans<<endl;//个数 gcd(n,d)//长度 n/gcd(n,d)//可以证明若在枚举时这个数已经 mark 过了,之后的数也肯定全部被标记了}return 0;
}