2020牛客多校第二场J题Just Shuffle(置换群欧几里得)

题意:给你一个n长的序列A,该序列由S{1,2,3,4...n}置换k次的,求置换一次的序列

题解:写这一题需要知道置换群的概念,下面讲一下什么是置换群(太菜讲不太清见谅)

S={1,2,3,4,5,6}

P={2,4,5,1,6,3}

那么置换一次的序列为

S1={2,4,5,1,6,3}

S2={4,1,6,2,3,5}

S3={1,2,3,4,5,6}

我们可以看到置换了3次后又恢复到了S。

1-2-4-1,3-5-6-3都是一个轮换

根据题意我们可以粗糙的概念SK=A

首先找出序列中所有轮换,每一个轮换即为一个环,长度记为len。显而易见A序列为环置换k % len次的结果,而最终答案为每个环置换一次的结果,即置换len * y + 1次的结果(y未知)。

可令一次操作置换t = k % len次,设进行x次操作会得到最终结果。(x未知)

由此可推出 t * x = y * len + 1,即 t * x + len * y = 1,欧几里得对x求最小正整数解即可。

 

#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const ll mod =1e9+7;
ll a[maxn],b[maxn];
vector<int> v;
int vis[maxn];
ll exgcd(ll a, ll b, ll& x, ll& y){
     if(!b) {y = 0; x = 1; return a;}
     int d=exgcd(b, a % b, y, x); 
     y -= (a / b) * x;
     return d;
}
int main(){
    IOS
    int n;
    ll k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    ll len=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            v.clear();
            int j=i;
            while(!vis[j]){
                vis[j]=1;
                v.push_back(j);
                j=a[j];
            }
            len=v.size();
            ll mid,c;
            ll g=exgcd(k,len,mid,c);
            mid=(mid%len+len)%mod;
            for(int j=0;j<len;j++){
                a[v[j]]=v[ (mid+j)%len ]; 
            }
//            ll z;
//            for(ll j=0;j<len;j++){
//                if( (k*j)%len == 1) z=j;
//            }
//            for(ll j=0;j<len;j++){
//                b[v[j]]=v[(j+z)%len];
//            }
        }
    }
    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }
    cout<<endl;
    
    
    return 0;
}
View Code

 

posted @ 2020-08-01 21:54  杰瑞与汤姆  阅读(194)  评论(0编辑  收藏  举报