J.Just Shuffle2020(模拟,置换) 牛客暑期多校训练营(第二场)

J.Just Shuffle2020(模拟,置换) 牛客暑期多校训练营(第二场)_第1张图片

题意:
按照 c [ i ] = b [ a [ i ] ] c[i]=b[a[i]] c[i]=b[a[i]]进行置换,给你起点排列和终点排列,置换了k次,求置换排列

思路:
模拟枚举环上节点,然后置换数组就对应 p [ b [ i ] ] = b [ i + 1 ] p[b[i]]=b[i+1] p[b[i]]=b[i+1],相当于在环上后移一位。

是道原题,详解可以看:
https://blog.csdn.net/tomjobs/article/details/107332494

只不过那道题是 a[1],a[2],a[3]…变成1,2,3,4,5…
这道题是1,2,3,4…变成a[1],a[2],a[3]…

把置换数组方向调一下就好了。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
const int maxn = 2e5 + 7;

int fa[maxn],cnt[maxn];
int a[maxn],b[maxn],p[maxn];

int findset(int x) {
    if(fa[x] == x) return x;
    return fa[x] = findset(fa[x]);
}

int gcd(int n,int m) {
    return m == 0 ? n : gcd(m,n % m);
}

int main() {
    int n,m;scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) {
        fa[i] = i;
        cnt[i] = 0;
    }
    
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
        int rx = findset(i),ry = findset(a[i]);
        if(rx != ry) fa[rx] = ry; //并查集维护环
    }
    
    for(int i = 1;i <= n;i++) {
        cnt[findset(i)]++;
    }
    
    for(int i = 1;i <= n;i++) { //本题m为质数,不会无解
        if(cnt[i] == 0) continue;
        if(gcd(m,cnt[i]) != 1) { //gcd不等于0的话没法获取环中节点
            break;
        }
    }
    
    for(int i = 1;i <= n;i++) {
        if(cnt[i] == 0) continue;
        int now = i;
        for(int j = 0;j < cnt[i];j++) {
            b[1ll * j * m % cnt[i]] = now; //获取环节点的真实位置
            now = a[now]; //枚举环中节点
        }
        b[cnt[i]] = b[0];
        for(int j = 1;j <= cnt[i];j++) {
            p[b[j]] = b[(j + 1) % cnt[i]]; //挪一位,刚好得到置换数组
        }
    }
    for(int i = 1;i <= n;i++) {
        printf("%d ",p[i]);
    }
    printf("\n");
    
    return 0;
}


你可能感兴趣的:(#,其他比赛题目,#,置换)