【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,可以得到一个同余方程: x r [ i ] ( m o d    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;
}


发布了141 篇原创文章 · 获赞 71 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sinat_40872274/article/details/104311474