题意:给出n张牌,标号为1-n,然后给出两个序列,序列1表示序列1,2,3,4……,n洗一次牌后到达的,序列2表示目标序列,问初始序列按序列1的洗牌方式洗几次能到达序列2的情况,如果不能到达输出-1。
题解:在初始序列和序列1的变换中找出1能变到那些牌,这些牌构成一个集合,这些集合中的牌必然是能够相互到达的,然后在序列2中也找出这样一个集合,集合中这些元素的相互顺序是要一样的,这就是判断能否达到,然后这样可以列出几个线性同余方程组,用中国剩余定理求解即可(顺便献上中国剩余定理模板)。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<map> #include<set> #include<queue> using namespace std; int n,a[1001],b[1001],vis[1001],c[1001],x[1001],y[1001]; long long exgcd(long long a,long long b,long long &x,long long &y) { if (b==0) { x=1; y=0; return a; } long long gcd=exgcd(b,a%b,x,y); long long t=x; x=y; y=t-a/b*x; return gcd; } long long china_remain(long long m,int a[],int b[]) { //for (int i=1;i<=m;i++) // cout<<a[i]<<" "<<b[i]<<endl; long long a1,a2,b1,b2,x,y,flag=0; a1=a[0]; b1=b[0]; int i; for (i=1;i<m;i++) { a2=a[i]; b2=b[i]; long long gcd=exgcd(a1,a2,x,y); if ((b2-b1)%gcd) { flag=1; break; } long long t=a2/gcd; x=(x*(b2-b1))/gcd; x=(x%t+t)%t; b1=a1*x+b1; a1=(a1*a2)/gcd; b1=(b1%a1+a1)%a1; } if (flag) return -1; return b1; } int main() { while (~scanf("%d",&n)) { if (n==0) break; int i; for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) scanf("%d",&b[i]); memset(vis,0,sizeof(vis)); int cnt=0; bool can=true; for (i=1;i<=n;i++) if (!vis[i]) { int flag=i; int count=0; while (!vis[flag]) { vis[flag]=1; c[count++]=flag; flag=a[flag]; } int pos=0; while (pos<count&&b[i]!=c[pos]) pos++; if (pos==count) { can=false; break; } x[cnt]=count; y[cnt++]=pos; //cout<<count<<" "<<pos<<endl; flag=a[i]; while (flag!=i) { if (b[flag]!=c[(++pos)%count]) { can=false; break; } flag=a[flag]; } if (!can) break; } if (!can) puts("-1"); else printf("%lld ",china_remain(cnt,x,y)); } }