环+逆元
——牛客多校赛第二场J题
Given a permutation with size n and an integerk, you should find a permutation substitutionP that{1,2,⋯,n} will become {A}A after performing substitution P for exactly k times. Print the permutation after performing P for once on{1,2,⋯,n}. If there are multiple solutions, print any of them. If there is no solution, print “-1” in one line.
输入描述:
The first line contains two integersn,k (1≤n≤10^5,10 ^8 ≤k≤10 ^9).
The second line contains n integers,
It is guaranteed that k is a prime number.
输出描述:
If there exists solutions, print n integers in one line, or print “-1” in one line.
示例1
输入
3 998244353
2 3 1
输出
3 1 2
题解:题意真的不好理解,如果没学过离散的话就很难弄,他就是离散里面的置换,他每一次 操作相当于对着乘原来置换(置换的几次幂)。这里题意中A^k=B,现在已知B和k要求就是求出原来的A,我们就是求出B需要作用几次会得到A即(B的几次方等于A )我先设 B的Z次方等于A,B又等于A^k次方所以A的K*Z幂等于A,所以Z就是等于K关于每个环大小的逆是多少。所以求出每个环的大小并且每个让K取逆(欧几里得算法哦),分别求出逆的大小后环里一循环就好啦!
#include<bits/stdc++.h>
using namespace std;
int a[500050],huan[500050],c,k,n,vis[500050];
void exgcd(int a,int b,int &x,int &y){
if(!b) {x=1,y=0;return ;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int num=1;
for(int i=1;i<=n;i++)
if(vis[i]==0){
vis[i]=1;
int tmp=a[i];
num=0;huan[num++]=i;//这里本来用num=1开始错了
//但是后来一想,下面求解的是取模o,所以从0-num-1
while(tmp!=i){
vis[tmp]=1;
huan[num++]=tmp;
tmp=a[tmp];
}
int x,y,t;
exgcd(k%num,num,x,y);
x=(x+num)%num;//这里就是求了K关于环大小的逆
for(int i=0;i<num;i++){
a[huan[i]]=huan[(i+x)%num];
}
}
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
}
}