题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3430
题意: 给一个初始顺序为1~N的牌组,然后给出一个置换和一个目标顺序牌组,问最少洗多少次可以变成目标牌组?
思路:
- 多次置换肯定会产生一个循环,我们求出第 i 位上的循环长度 p[i] 和第 i 位第一次变成目标顺序的长度 r[i].
可以解释为:第 i 位经过了 ki * p[i] + r[i] 次的置换可以变成目标顺序。 - 设我们的答案是x,可以得到一个同余方程:
一共 n 位,所以我们可以得到一个有 n 个同余方程的同余方程组,因为 p[i] 可能并非两两互质,所以要使用扩展中国剩余定理来解同余方程组。 - 如果同余方程组无解输出’-1’,否则输出 x
/*****************************
*author:ccf
*source:HDU-3430
*topic:CRT
*******************************/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#define ll long long
using namespace std;
const int N = 1005;
int n,p[N],r[N];
ll s[N],target[N];
bool bk[N];
void get_loop(){
for(int i = 1; i <= n; ++i){
int t = i;
memset(bk,false,sizeof bk);
while(!bk[t]){
bk[t] = true;
p[i]++;
t = s[t];
if(!r[i] && t == target[i])
r[i] = p[i];
}
}
}
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b == 0){
x = 1;
y = 0;
return a;
}
ll t = exgcd(b,a%b,x,y);
ll x0 = x,y0 = y;
x = y0;
y = x0-a/b*y0;
return t;
}
ll CRT(){
for(int i = 1;i <= n; ++i)
r[i] %= p[i];
ll flag = 0,x,y,g,x0,pt,rt;
pt = p[1],rt = r[1];
for(int i = 2;i <= n; ++i){
g = exgcd(pt,p[i],x,y);
if((r[i] - rt) % g){
flag = 1;
}else{
x0 = (r[i]-rt) / g * x % (p[i]/g);
rt = x0*pt + rt;
pt = pt / g * p[i];
}
rt = (rt % pt + pt) % pt;
}
if(flag) return -1;
else return rt;
}
int main(){
freopen("data.in","r",stdin);
ll ans = 0;
while(scanf("%d",&n) && n){
ans = 0;
memset(p,0,sizeof p);
memset(r,0,sizeof r);
for(int i = 1; i <= n; ++i) scanf("%lld",s+i);
for(int i = 1; i <= n; ++i) scanf("%lld",target+i);
//找出循环长度 和 余数
get_loop();
//使用扩展中国剩余定理求出答案
ans = CRT();
printf("%lld\n",ans);
}
return 0;
}