題解 Aizu2970 【Permutation Sort】
阿新 • • 發佈:2020-07-27
題目大意
給你兩個 \(n\) 個整數的排列,第一個排列表示原排列,第二個排列表示第 \(i\) 個數可以和i變成第 \(g_i\) 個數,問,最少對所有數進行幾次操作可以使原排列變為有序的排列。
題解
首先,我們可以利用第二個排列建圖,易得每一個點只有一個出度,一個入度,所以這幅圖只由簡單環和自環組成。
我們還可以發現,在環上跑大於環的長度的距離等同於跑兩點之間的直線距離,也就是說如果環的長度為 \(cnt_i\) ,兩點之間的直線距離為 \(x_i\) ,我們要求的距離為 \(d\) ,那麼 $d\equiv x_i(mod~cnt_i) $ 。根據每一個 \(i\) ,我們都可以列出這麼一個方程,於是問題就轉變為求 \(n\)
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define int long long const int N=205; int n; int a[N],to[N]; int e[N][N]; int tag[N],cnt[N]; void dfs(int p,int nam) { tag[p]=nam; cnt[nam]++; if(!tag[to[p]]) dfs(to[p],nam); return ; } int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } void exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1,y=0; return ; } exgcd(b,a%b,x,y); int tmp=x; x=y; y=tmp-a/b*y; } signed main() { cin>>n; for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) e[i][j]=1e18+5; for(int i=1;i<=n;++i) { cin>>to[i]; e[i][to[i]]=1; } for(int i=1;i<=n;++i) e[i][i]=0; for(int k=1;k<=n;++k) { for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) e[i][j]=min(e[i][j],e[i][k]+e[k][j]); } } for(int i=1;i<=n;++i) { if(e[a[i]][i]>=1e18+5) { printf("-1\n"); return 0; } } for(int i=1;i<=n;++i) { if(!tag[i]) dfs(i,i); } int M=cnt[tag[1]],ans=e[a[1]][1]; for(int i=2;i<=n;++i) { int x,y,tmp=gcd(M,cnt[tag[i]]),now=((e[a[i]][i]-ans)%cnt[tag[i]]+cnt[tag[i]])%cnt[tag[i]]; if(now%tmp) { printf("-1\n"); return 0; } exgcd(M,cnt[tag[i]],x,y); x*=now/tmp; ans+=x*M; M*=cnt[tag[i]]/tmp; ans=(ans%M+M)%M; } printf("%lld\n",ans); return 0; }