1. 程式人生 > 實用技巧 >2020牛客多校第二場J題Just Shuffle(置換群歐幾里得)

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