人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

题解:Coprime Permutation

前言

一种很新的排列计数。

思路分析

考虑将排列视作映射,初始令 pi=i,考虑哪些位置能够进行交换。

si 表示 pi 的质因子集合,为了方便,考虑用元素乘积刻画这个集合。

不难发现,对于 si=sj 的任意位置 (i,j),它们是等价的,也就是说它们是可以任意交换不影响排列的合法性。

感性理解就是既然质因子集合相同,那么和谁求 gcd 不影响答案。

然后很遗憾发现没做完,还有其他位置可以进行交换。

经过手模发现,对于两个质数 pi=pj,如果 npi=npj,也就是说 pipj[1,n] 的倍数个数相同,那么这些倍数可以两两对应交换。

也就是说,对于 ai=0 的情况,是第一种情况每个等价类的大小的阶乘,乘上第二种情况每个等价类的大小的阶乘。

现在考虑,ai 的某些位置被填上了数。

如果 aii 的匹配不矛盾的话,那么我们只需要在两种情况的对应等价类中删去一个元素就行。

但是有很多种情况,aii 的匹配会导致不存在合法排列,因此我们需要讨论一下什么时候 aii 的匹配不合法。

  • aii<n 的质因子集合不相同;

  • aiin 的质因子的存在性不同;

  • aiin 的质因子在 [1,n] 中的倍数个数不同。

  • aii 在第二种情况中的映射和之前的映射矛盾。

做完了。

使用递推质因数分解,总体复杂度 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;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18700812

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(27)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起