题目大意
给定置换,求有多少个排列可以通过若干次这个置换,变成1到n的排列。
解题思路
- 如果一个置换中没有环,如{1,2,3},只能找出一个符合条件的原排列:{1,2,3}。
- 如果有一个环,如{3,1,2},由于环的长度为3(不同元素的数量),所以可以找出三种原排列:{1,2,3},[2,3,1},{3,1,2}。
- 如果有两个环,如{2,1,4,3},由于两个环的长度分别为2(不同元素的数量),所以可以找出两种原排列:{1,2,3,4},[2,1,4,3}。
不难得出,如果有一个长度为n的环,那么可以找出n个原序列;而有多个环的时候,可以找出这些环长度的lcm。由于长度总和就是n,所以他们的lcm一定不会大于n位,不需要取模。
AC代码
#include<bits/stdc++.h> using namespace std; int n,m,l=1,t,a[100010],b[100010],v[100010],p[100010],r[100010],ans[100010]; void multi(int x) { int i; for(i=1;i<=l;i++) ans[i]*=x; for(i=1;i<l;i++) if(ans[i]>9) ans[i+1]+=ans[i]/10,ans[i]%=10; while(l<n && ans[l]>9) ans[l+1]+=ans[l]/10,ans[l++]%=10; ans[l+1]=0; } int main() { int x,i,j; ans[0]=0,ans[1]=1; for(i=2;i<=100000;i++) if(!p[i]) { for(j=i*2;j<=100000;j+=i) p[j]=1; r[++t]=i; } scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=1;i<=n;i++) { if(v[i]) continue; v[i]=b[++m]=1,j=a[i]; while(j!=i) v[j]=1,b[m]++,j=a[j]; } memset(p,0,sizeof(p)); for(i=1;i<=m;i++) for(j=1;j<=t;j++) { if(b[i]<=1) break; x=0; while(b[i]%r[j]==0) x++,b[i]/=r[j]; p[r[j]]=max(p[r[j]],x); } for(i=1;i<=t;i++) while(p[r[i]]--) multi(r[i]); for(i=l;i>=1;i--) printf("%d",ans[i]); return 0; }