2020牛客暑期多校训练营(第二场) ( J Just Shuffle 置换群)

题目链接

题意:给定  排列 (1,2,3,4,5...n)  执行 p 置换  进行k次得到排列B。现求 p置换  输出排列(1,2,3,4,5......n)进行一次p置换

做法:补题参考来自:博客

有很多疑问,甚至怀疑他的公式是不是写错了。

问题1、第六行公式应该是 B^Z=B^Z=A^{Z*K}

问题2、为什么上面的公式是A^{Z*K},不是A^{Z+K} 

认为是Z+K的肯定是误认为  B 进行Z次的置换是P置换,其实不然

因为我们是直接对排列B  向排列(1,2,3,4,...n) 置换一次。

因此B进行一次的置换 规则 是 不同于P 的。

这次的置换规则就是每个循环节  进行 K%r 次p置换。

从而可以推出公式  若想得到 排列A  进行  Z次 (K%r)次的P置换  使得Z*(K%r)%r==0

根据输出要求  公式变为:Z*(K%r)%r==1

问题3、为什么排列B 单独的循环节可以单独的执行Z次置换呢?

其实很好理解。对于所有的 Zi  求一个LCM ,对大的排列 执行LCM 次置换,也就是对所有循环节 进行了Zi次置换。

代码就没有自己写了。参考上面博客的代码即可。

代码来自:博客

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;

int vs[M],a[M],b[M];
vector<int>v;
int n,k;
ll powmod(ll a,ll b, ll mod)
{
    ll res = 1;
    for(; b; b>>=1){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}
void gao()
{
	int r=v.size(),inv;

	if(r<3) for(int i=0;i<r;i++)if((ll)k*i%r==1)inv=i;
	else inv = powmod(k, r-2, r);
	//printf("inv:%d t:%lld\n",inv,t);
	for(int i=0;i<r;i++)b[v[i]]=v[(i+inv)%r];
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];// P ^ K = A
	//假设我们要求置换A ,z次
	//A ^ z =P^(K*z)  其中 : K*z % r == 1 //因为置换取模r为0时刚好是 1 2 3 ……,需要再置换一次
	//而K*z % r == 1  z=K^(-1),即K的逆元

	for(int i=1;i<=n;i++)
	{
		if(!vs[i])
		{
			v.clear();
			int x=a[i];
			while(!vs[x])//a 变到1,2,3,4......n
			{
				vs[x]=1;
				v.pb(x);
				x=a[x];
			}
			gao();
		}
	}
	for(int i=1;i<=n;i++)printf("%d ",b[i]);
	puts("");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/107378776
今日推荐