[关键字]:置换群 路径压缩
[题目大意]:生成数列c0..n-1: c0=0, ci+1=(ci*q+p) MOD m。你需要确定非负整数x1..n-1, y1..n-1。使得pos0=s, posi=(ci+d*xi+yi) MOD n恰好是一个0到n-1的排列,当(xi,yi)有多种选择时,yi要尽量小,然后xi尽量小。通过这种方法我们可以得出唯一的pos0..n-1。假设有n-1个物品1..n-1 posi表示物品i的位置 pos0表示空位的位置。初始时空位在0号位,物品i在i号位。每次移动可以把一个物品放到空位去,然后它原来所在的位置变成空位。问从初始移动到目标pos最少需要多少移动。
//====================================================================================================================================
[分析]:首先第二问是一个经典的置换群的问题,如果一个置换群的循环s里有0,则需要|s|-次如果没有0则需要|s|-1+2(0换进换出)。而第一问需要用调整的方法,注意到根据公式x每加一就会后移d%n,y每加一就会后移1%n。要求y最小可以先从x开始调整,x所有能到达得位置有n/gcd(d%n,n)个所以对x的调整相当于在这个环里选出没有用过的数,而y则是在x的同余类的环里跳跃,一个环用完了就变到下一个环。利用类似并查集的压缩和查找可以降低复杂度。详细见HNOI2010题解压缩包。
[代码]:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAXN=120100; int n,m,s,q,p,d,test; int c[MAXN],pos[MAXN],x[MAXN],y[MAXN],a[MAXN]; int rx[MAXN],ry[MAXN],rx_cnt[MAXN],ry_cnt[MAXN],cnt[MAXN]; bool b[MAXN]; int GCD(int a,int b) { if (a<b) swap(a,b); int t; while (b) t=a,a=b,b=t%b; return a; } void Work(int x) { int x2; if ((x2=x+d)>=n) x2-=n; rx[x]=x2; rx_cnt[x]=1; x%=m; if (--cnt[x]) return; if ((x2=x+1)>=m) x2-=m; ry[x]=x2; ry_cnt[x]=1; } int Find(int *r,int *r_cnt,int x) { if (r[x]==x) return 0; r_cnt[x]+=Find(r,r_cnt,r[x]); r[x]=r[r[x]]; return r_cnt[x]; } int main() { freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); scanf("%d",&test); while (test--) { scanf("%d%d%d%d%d%d",&n,&s,&q,&p,&m,&d); c[0]=0; for (int i=1;i<n;++i) c[i]=((long long)c[i-1]*q+p)%m; d%=n; m=GCD(d,n); for (int i=0;i<m;++i) { cnt[i]=n/m; ry[i]=i; ry_cnt[i]=0; } for (int i=0;i<n;++i) rx[i]=i,rx_cnt[i]=0; Work(pos[0]=s); for (int i=1;i<n;++i) { y[i]=Find(ry,ry_cnt,c[i]%m); x[i]=Find(rx,rx_cnt,(c[i]+y[i])%n); pos[i]=(c[i]+(long long)x[i]*d+y[i])%n; Work(pos[i]); } for (int i=0;i<n;++i) a[i]=pos[i]; int ans=0; memset(b,0,sizeof(b)); for (int i=0;i<n;++i) if (!b[i]) { int t=0,p=i; while (!b[p]) b[p]=1,++t,p=a[p]; if (t!=1) ans+=t-1+(i!=0)*2; } printf("%d\n",ans); } fclose(stdin),fclose(stdout); return 0; }