题意:求有多少种符合要求的排列满足对于所有i,j,当gcd(i,j)=1时,gcd(pi,pj)=1。
排列上的一些位置给出。
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod=1e9+7; 5 const int N=1000005; 6 int n,p[N],cnt[N],Cnt[N],base[N],To1[N],To2[N],jc[N],x,ans; 7 vector<int> fac[N]; 8 void fail(){puts("0");exit(0);}//这个东西超级好用! 9 void shai() 10 { 11 for (int i=2;i<=n;i++) 12 if (!p[i]) 13 { 14 cnt[n/i]++; 15 for (int j=i;j<=n;j+=i) 16 { 17 p[j]=1;base[j]*=i; 18 fac[j].push_back(i); 19 } 20 } 21 } 22 void check(int x,int y) 23 { 24 if (fac[x].size()!=fac[y].size()) fail(); 25 for (int i=0;i<fac[x].size();i++) 26 { 27 int fu=fac[x][i],fv=fac[y][i]; 28 int u=(x==1)?1:n/fu,v=(y==1)?1:n/fv; 29 if (u!=v) fail(); 30 if (To1[fu]&&To1[fu]!=fv) fail(); 31 if (To2[fv]&&To2[fv]!=fu) fail(); 32 if (!To2[fv]) To1[fu]=fv,To2[fv]=fu,cnt[u]--; 33 } 34 Cnt[base[x]]--; 35 } 36 int main() 37 { 38 scanf("%d",&n); 39 jc[0]=1;cnt[1]=1;//1和所有数互质!!! 40 for (int i=1;i<=n;i++) jc[i]=(ll)jc[i-1]*i%mod,base[i]=1; 41 shai();fac[1].push_back(1);//!!! 42 for (int i=1;i<=n;i++) Cnt[base[i]]++; 43 for (int i=1;i<=n;i++) 44 { 45 scanf("%d",&x); 46 if (x) check(i,x); 47 } 48 ans=1; 49 for (int i=1;i<=n;i++) 50 ans=(ll)ans*jc[cnt[i]]%mod*jc[Cnt[i]]%mod; 51 printf("%d\n",ans); 52 return 0; 53 }
易错点:1.注意1和所有数互质,所以cnt[1]=1,表示1~n和1不互质的只有1个数。
2.fac[1].push_back(1),1有一个因数为1,小心判错。
题解:数学+性质
一开始我想分别求出与每个数互质的数的个数,较难。
发现可以从什么样的数相互交换等价入手:1.两个数的因数种类完全一样。2.若质数p1,p2,且[n/p1]=[n/p2]时,即1~n中所有p1的倍数和p2的倍数可以一一对应,那么对应互换。这两个交换相互独立。
如果没有固定元素这样就结束了。
判断合法性:
1.两个元素的因数去重后的个数要一样。
2.两个限制可以合并为对应因数的出现次数一样。
3.质因子之间产生轮换,可以会产生矛盾,要判掉。
最后减去已经确定的答案。
实现的时候有一些小技巧:
1.比较因数种类完全一样时,相当于比较两个数所有质因子的一次乘积。
2.可以用素数筛求出所有质数并筛出每个数的因数种类。
3.当p1,p2<=n^0.5时,[n/p1]与[n/p2]必然不等。