题目大意

给一个长度为n的排列A={1,2,3,...,n}以及置换的次数k,在对A使用k次置换P后得到新的排列B。(整理:A是原排列,P是置换,B是目标排列,k是次数
输入n,k和B,输出A,如果无解输出-1。(规定k是大质数,108k109说明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;
}