Just Shuffle——2020牛客暑期多校训练营(第二场)J题

题目描述

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;
}

猜你喜欢

转载自blog.csdn.net/cxkdad/article/details/107359254