前言
一种很新的排列计数。
思路分析
考虑将排列视作映射,初始令 \(p_i=i\),考虑哪些位置能够进行交换。
设 \(s_i\) 表示 \(p_i\) 的质因子集合,为了方便,考虑用元素乘积刻画这个集合。
不难发现,对于 \(s_i=s_j\) 的任意位置 \((i,j)\),它们是等价的,也就是说它们是可以任意交换不影响排列的合法性。
感性理解就是既然质因子集合相同,那么和谁求 \(\gcd\) 不影响答案。
然后很遗憾发现没做完,还有其他位置可以进行交换。
经过手模发现,对于两个质数 \(p_i=p_j\),如果 \(\lfloor \frac{n}{p_i} \rfloor= \lfloor \frac{n}{p_j} \rfloor\),也就是说 \(p_i\) 的 \(p_j\) 在 \([1,n]\) 的倍数个数相同,那么这些倍数可以两两对应交换。
也就是说,对于 \(a_i=0\) 的情况,是第一种情况每个等价类的大小的阶乘,乘上第二种情况每个等价类的大小的阶乘。
现在考虑,\(a_i\) 的某些位置被填上了数。
如果 \(a_i\) 和 \(i\) 的匹配不矛盾的话,那么我们只需要在两种情况的对应等价类中删去一个元素就行。
但是有很多种情况,\(a_i\) 和 \(i\) 的匹配会导致不存在合法排列,因此我们需要讨论一下什么时候 \(a_i\) 和 \(i\) 的匹配不合法。
-
\(a_i\) 和 \(i\) 的 \(<\sqrt{n}\) 的质因子集合不相同;
-
\(a_i\) 和 \(i\) 的 \(\ge \sqrt{n}\) 的质因子的存在性不同;
-
\(a_i\) 和 \(i\) 的 \(\ge \sqrt{n}\) 的质因子在 \([1,n]\) 中的倍数个数不同。
-
\(a_i\) 和 \(i\) 在第二种情况中的映射和之前的映射矛盾。
做完了。
使用递推质因数分解,总体复杂度 \(O(kn)\),\(k\) 是小于 \(7\) 的常数。
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,a,ans,p1[1000005],ip[1000005],p2[1000005],fac[1000005],C[1000005],c[1000005],mp[1000005];
int prime[1000005],isprime[1000005],cnt;
void seive(){for(int i=2;i<=n;i++){if(!isprime[i]) prime[++cnt]=i,mp[i]=i,ip[i]=n/i;for(int j=1;j<=cnt && i*prime[j]<=n;j++){isprime[i*prime[j]]=1;mp[i*prime[j]]=prime[j];if(i%prime[j]==0) break;}}
}
vector<int> v[1000005];
int mul[1000005];
void work(int m){int a=m;mul[m]=1;for(int i=2;i*i<=a;i++){if(a%i==0){while(a%i==0) a/=i;v[m].push_back(i);mul[m]*=i;}}if(a>=2){v[m].push_back(a);mul[m]*=a;}sort(v[m].begin(),v[m].end());c[mul[m]]++;
}
signed main(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n;seive();fac[0]=1;for(int i=1;i<=n;i++){fac[i]=fac[i-1]*i%mod;} mul[1]=c[1]=C[1]=ip[1]=1;for(int i=1;i<=cnt;i++){C[ip[prime[i]]]++;}v[1].push_back(1);for(int i=2;i<=n;i++){int tmp=i;mul[i]=1;while(tmp!=1){int p=mp[tmp];while(tmp%p==0) tmp/=p;v[i].push_back(p);mul[i]*=p; }c[mul[i]]++;}for(int i=1;i<=n;i++){cin>>a;if(a==0) continue;if(v[a].size()!=v[i].size()){cout<<0;return 0;}for(int j=0;j<v[a].size();j++){if(ip[v[a][j]]!=ip[v[i][j]]){cout<<0;return 0;}}int x=v[i].back(),y=v[a].back();if(p1[x] && p1[x]!=y){cout<<0;return 0;}if(p2[y] && p2[y]!=x){cout<<0;return 0;}if(!p1[x] && !p2[y]){p1[x]=y;p2[y]=x;C[ip[y]]--;}c[mul[a]]--;} ans=1;for(int i=1;i<=n;i++){ans=ans*fac[C[i]]%mod*fac[c[i]]%mod;}cout<<ans;return 0;
}