【HDU3430】Shuffling 置換的循環節 + 擴展中國剩餘定理

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3430
題意: 給一個初始順序爲1~N的牌組,然後給出一個置換和一個目標順序牌組,問最少洗多少次可以變成目標牌組?
思路:

  1. 多次置換肯定會產生一個循環,我們求出第 i 位上的循環長度 p[i] 和第 i 位第一次變成目標順序的長度 r[i].
    可以解釋爲:第 i 位經過了 ki * p[i] + r[i] 次的置換可以變成目標順序。
  2. 設我們的答案是x,可以得到一個同餘方程:xr[i](mod  p[i])x\equiv r[i](mod \ \ p[i])
    一共 n 位,所以我們可以得到一個有 n 個同餘方程的同餘方程組,因爲 p[i] 可能並非兩兩互質,所以要使用擴展中國剩餘定理來解同餘方程組。
  3. 如果同餘方程組無解輸出’-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;
}


發佈了132 篇原創文章 · 獲贊 54 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章