说明:第1个到第7个盒子的最终位置依次是:2 5 6 4 1 0 7 计算过程可能超过整型范围。
题解:并查集+置换
这道题的重点应该是pos数组的构造,因为我们要在y最小的情况下再满足x最小,有一种很暴力的方法,就是y从0开始枚举,然后判断在(c[i]+y)%n的意义下有没有可行的x,不断+d的过程实际会形成一个或者多个环,我们其实就是要判断包含这个点的环中是否还有没有用过的元素。每次必然不能一个一个的在环上跳,所以如果一个元素入选,那么我们就用并查集将当前权x与x+d并到一起。
然后剩下的就是置换的锅了,对于这道题来说,如果轮换中只有1个元素,那么直接不用动。如果轮换中包含空位置,那么可以用len-1将其搞成正常的顺序,如果不包含空位置我们首先要把空位置引入,用完了再换回去,需要len+1步操作。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 200003 #define LL long long using namespace std; LL fa[N],c[N],pos[N],T,n,m,p,q,d,s,mark[N],use[N]; LL find(int x) { if (fa[x]==x) return x; fa[x]=find(fa[x]); return fa[x]; } int main() { freopen("a.in","r",stdin); scanf("%d",&T); while (T--) { scanf("%lld%lld%lld%lld%lld%lld",&n,&s,&q,&p,&m,&d); c[0]=0; for (int i=1;i<n;i++) c[i]=(c[i-1]*q+p)%m; //for (int i=1;i<=n;i++) cout<<c[i]<<" "; //cout<<endl; memset(mark,0,sizeof(mark)); mark[s]=1; for (int i=0;i<=n*2;i++) fa[i]=i; fa[s]=(s+d)%n; pos[0]=s; for (int i=1;i<n;i++) { LL y=0; int t=find((c[i]+y)%n); while (mark[t]) { y++; t=find((c[i]+y)%n); } pos[i]=t; mark[t]=1; int r1=find(t); int r2=find((t+d)%n); fa[r1]=r2; } //for (int i=0;i<n;i++) cout<<pos[i]<<" "; //cout<<endl; LL ans=0; memset(use,0,sizeof(use)); for (int i=0;i<n;i++) if (!use[i]) { int j=i; int len=0; bool pd=true; while (!use[j]) { if (j==0) pd=false; len++; use[j]=1; j=pos[j]; } if (len==1) continue; if (!pd) len--; else len++; //cout<<len<<endl; ans=ans+len; } printf("%lld\n",ans); } }