题目链接
题意:给定 排列 (1,2,3,4,5...n) 执行 p 置换 进行k次得到排列B。现求 p置换 输出排列(1,2,3,4,5......n)进行一次p置换
做法:补题参考来自:博客
有很多疑问,甚至怀疑他的公式是不是写错了。
问题1、第六行公式应该是
问题2、为什么上面的公式是,不是
认为是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;
}