Just Shuffle——2020牛客暑期多校训练营(第二场)J题
- 题目描述
- 输入描述
- 输出描述
- 输入
- 输出
- 题解
- 1.[逆元](https://blog.csdn.net/Harington/article/details/96310239)(一般以[扩展欧几里得,费马小定理或欧拉定理,特例,打表等方法求解](https://blog.csdn.net/guhaiteng/article/details/52123385))
- 2.[置换群快速幂](https://wenku.baidu.com/view/0bff6b1c6bd97f192279e9fb.html)(3.1.a中可以得到长度与指数互质时的分数幂运算的过程是,目标循环每次指针向后移k位,源循环每次向后移1位)
- AC代码
题目描述
Given a permutation with size n and an integer k, you should find a permutation substitution P that {1,2,⋯,n} will become 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.
输入描述
输出描述
If there exists solutions, print n integers in one line, or print “-1” in one line.
输入
3 998244353
2 3 1
输出
3 1 2
题解
本题有两种解法:
1.逆元(一般以扩展欧几里得,费马小定理或欧拉定理,特例,打表等方法求解)
2.置换群快速幂(3.1.a中可以得到长度与指数互质时的分数幂运算的过程是,目标循环每次指针向后移k位,源循环每次向后移1位)
(以下是官方题解)
AC代码
官方解法(来自大佬乐正绫ovo)
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
template <typename T> bool chkmax(T &x,T y){return x<y?x=y,true:false;}
template <typename T> bool chkmin(T &x,T y){return x>y?x=y,true:false;}
int readint(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k;
int a[100005],ans[100005];
bool vis[100005];
void exgcd(int x,int y,int &a,int &b){
if(!y){
a=1,b=0;
return;
}
exgcd(y,x%y,b,a);
b-=a*(x/y);
}
int main(){
n=readint(); k=readint();
for(int i=1;i<=n;i++) a[i]=readint();
for(int i=1;i<=n;i++){
if(vis[i]) continue;
vis[i]=1;
vector<int> v(0);
v.pb(i);
for(int j=a[i];!vis[j];j=a[j]) v.pb(j),vis[j]=1;
int tmp=k%v.size(),x,y;
exgcd(tmp,v.size(),x,y);
if(x<0) x+=v.size();
for(int j=0;j<v.size();j++) ans[v[j]]=v[(j+x)%v.size()];
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
暴力求逆元
#include<bits/stdc++.h>
using namespace std;
int vis[100007],a[100007],b[100007],n,k;vector<int>lo;
int main()
{
cin>>n>>k;for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)if(!vis[i]){lo.clear();int x=a[i];while(!vis[x])vis[x]=1,lo.push_back(x),x=a[x];int r=lo.size(),inv;for(int i=0;i<r;i++)if((long long)k*i%r==1)inv=i;for(int i=0;i<r;i++)b[lo[i]]=lo[(i+inv)%r];}
for(int i=1;i<=n;i++)cout<<b[i]<<" ";
}
置换群快速幂(来自大佬Alchemist)
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n, k, a[100005], ans[100005];
bool vis[100005];
void getCircle(int x) {
vector<int> V;
V.push_back(x);
x = a[x];
while (x != V[0]) {
V.push_back(x);
vis[x] = true;
x = a[x];
}
int r = k % (int) V.size();
vector<int> R(V.size());
for (int i = 0; i < V.size(); i++)
R[(i * r) % V.size()] = V[i];
for (int i = 0; i < V.size(); i++)
ans[R[i]] = R[(i + 1) % V.size()];
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
if (!vis[i])
getCircle(i);
for (int i = 1; i < n; i++)
printf("%d ", ans[i]);
printf("%d", ans[n]);
return 0;
}