题意
给定一堆个数为
n
n
的牌堆,一开始第 张牌的数字是
i
i
。你有一个坏掉的洗牌机,它一次操作只能把在 位置的牌洗到
Ai
A
i
位置。你希望得到一个牌堆,使数字为
i
i
的牌在 的位置。求最少需几次操作,如果做不到,输出
−1
−
1
。
1≤n≤520
1
≤
n
≤
520
思路
将每张牌分开来讨论,对于数字为 i i 的牌,第一次洗到 位置时的洗牌次数设为 CRT C R T 的 A A ,首次出现循环的洗牌次数设为 的 P P 。将每张牌的 值合并即可。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
int shuff[523],goal[523];
struct CRT
{
LL A,P;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
return;
}
LL Exgcd(LL A,LL B,LL C)
{
LL k1,k2,g=gcd(A,B);
if(C%g)return -1;
A/=g,B/=g,C/=g;
exgcd(A,B,k1,k2);
return (k1*C%B+B)%B;
}
bool kase_insert(CRT _)
{
LL res=Exgcd(P,_.P,_.A-A);
if(!~res)return 0;
A+=res*P;
P=P/gcd(P,_.P)*_.P;
return 1;
}
};
int main()
{
int n;
while(scanf("%d",&n),n)
{
CRT sum;
FOR(i,1,n)scanf("%d",&shuff[i]);
FOR(i,1,n)scanf("%d",&goal[i]);
bool flag=0,mark[523];
FOR(i,1,n)
{
memset(mark,0,sizeof(mark));
int p=i,step=0,f;
while(!mark[p])
{
mark[p]=1;
if(p==goal[i])f=step;
p=shuff[p];
step++;
}
if(i!=1)
{
if(!sum.kase_insert((CRT){f,step}))
flag=1;
}
else sum=(CRT){f,step};
if(flag)break;
}
printf("%lld\n",flag?-1:sum.A);
}
return 0;
}