题目大意
给一个长度为n的排列A={1,2,3,...,n}以及置换的次数k,在对A使用k次置换P后得到新的排列B。(整理:A是原排列,P是置换,B是目标排列,k是次数)
输入n,k和B,输出A,如果无解输出-1。(规定k是大质数,108≤k≤109,说明K一定存在逆元,不会出现无解的情况,所以这句话多余了(不过能做得出来的,做着做着都会发现))
解题思路
这道题需要置换群概念来分析,不知道的(我做之前也完全不了解)可以先看一下https://blog.csdn.net/y990041769/java/article/details/45172095.(转载)
接下来的表述有点绕,但是应该能读懂(借鉴了其他数篇大佬题解)
我们通过题面可以得知这样一个粗糙的概念:Ak=B
接下来假设z为k的逆元,将B置换z次,再设r为P的置换循环节,当z*k%r==0时,就能得到A,得到Bz=A。
因为题目求的就是A,所以只要求出z即可。
因为Bk=Ak*z,z*k%r=0形成循环,又回到了A。
那么我们令z*k%r==1,这时就可以求出z,将B置换z次得到A。对于B来说,我们每个环分开来处理,每个环的循环节即环的大小r。
AC代码
在这里用了vector来存储环。
#include<bits/stdc++.h>
using namespace std;
long long a[100010],b[100010],c[100010],n,k;
vector<long long> v;
void fff()
{
long long x=v.size(),y,j;
for(j=0;j<x;j++)
if((k*j)%x==1) y=j;
for(j=0;j<x;j++)
b[v[j]]=v[(j+y)%x];
}
int main()
{
long long x,i;
scanf("%lld%lld",&n,&k);
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(i=1;i<=n;i++)
{
if(c[i]) continue;
v.clear();
x=a[i];
while(!c[x])
{
v.push_back(x);
c[x]=1,x=a[x];
}
fff();
}
for(i=1;i<=n;i++) printf("%lld ",b[i]);
return 0;
}