題目鏈接: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;
}