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求最小正整数解即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }