C/C++教程

Codeforces 698F - Coprime Permutation(找性质)

本文主要是介绍Codeforces 698F - Coprime Permutation(找性质),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Codeforces 题面传送门 & 洛谷题面传送门

u1s1 感觉这个 D1F 比某道 jxd 作业里的 D1F 质量高多了啊,为啥这场的 D 进了 jxd 作业而这道题没进/yun

首先这题肯定有个结论对吧,那么我们就先尝试猜一下什么样的排列符合条件,也就是先考虑这题 \(a_i\)​​ 全是 \(-1\)​​ 的情况怎么做,那么通过观察可以发现,由于判定两个数是否互质的过程中只需要考虑它们的质因子集合即可,因此可以发现如果两个数包含的质因子集合完全相同,那么它们显然是可以互换的,因此假设第 \(i\)​​ 种质因子集合有 \(c_i\)​​ 个数,对于全 \(-1\)​​ 的情况贡献应乘上 \(\prod c_i!\)​​。没了吗?手玩一下样例发现对于 \(n=5\)​​ 的情况 \(3,5\)​​ 也可以交换,我们不妨来分析一波,对于 \(n=5\)​​ 的情况 \(3,5\)​​ 满足 \(\lfloor\dfrac{5}{3}\rfloor=\lfloor\dfrac{5}{5}\rfloor\)​​,也就是说我们可以将 \(5\)​​ 的倍数与 \(3\)​​ 的倍数整体互换,类似地,对于两个质数 \(p_i,p_j\)​​,如果它们满足 \(\lfloor\dfrac{n}{p_i}\rfloor=\lfloor\dfrac{n}{p_j}\rfloor\)​​,那么我们对于 \(k\in[1,\lfloor\dfrac{n}{p_i}\rfloor]\)​​,将 \(p_i·k\)​​ 出现的位置与 \(p_j·k\)​​ 出现的问题交换,不难证明交换前任意两个位置是否互质的情况相同,因此如果交换前满足条件,交换后也满足条件,因此如果再记 \(C_i\)​ 表示有多少个质数 \(p\)​ 满足 \(\lfloor\dfrac{n}{p}\rfloor=i\)​,那么答案还要再乘上 \(\prod C_i!\)​。这样已经是充要条件了吗?没错,打个表可以发现所有符合要求都符合上述两个条件。

然后我打表只看出了第一个结论,然后第二个结论以为 \(>\dfrac{n}{2}\) 的质数也可视作相同,然后就愉快地 WA 12 了

然后考虑存在一个 \(a_i\) 固定的情况,显然对于这样的情况,我们还要找排列中的数在换来换去的过程中,有没有什么量是不变的,显然我们的操作只可能是交换两个质数,不会改变每个位置的 \(a_i\) 的质因子个数,因此如果 \(a_i\) 质因子个数不等于 \(i\) 质因子个数就直接 \(-1\)。同理交换前后,是不改变 \(n\) 除以对应质因子下取整的值的,因此如果我们发现 \(i\) 与 \(a_i\) 中,\(n\) 除以对应质因子下取整的值不同,那也可以直接 \(-1\)。那还有没有需要 \(-1\) 的情况呢?不难发现如果存在一个质因子已经被钦定要映射到别的质因子上,而在 \(i\) 与 \(a_i\) 中这种映射关系出现了冲突,那么也应是 \(-1\)。这里有一个投机取巧(bushi)的技巧,就是显然能够交换的质因子只可能 \(>\sqrt{n}\),而一个 \(\le n\) 的数中最多只有一个 \(>\sqrt{n}\)​ 的质因子,因此对于这种情况我们只用检验最大的质因子是否满足条件即可。如果不存在上述三种情况那肯定是有解的,不同于 \(a_i\) 全是 \(-1\) 的情况的一点是,如果我们发现 \(a_i\ne 0\),那么我们就找出 \(a_i\) 的质因子集合,然后令对应的 \(c_i\) 减 \(1\),然后如果我们找出了一对质因子的映射关系,就将对应的 \(C_i\) 减 \(1\),然后还是输出 \(\prod c_i!·\prod C_i!\)​ 即可。

时间复杂度 \(\sum\limits_{i=1}^n\omega(i)<\mathcal O(7·n)=\mathcal O(n)\)

const int MAXN=1e6;
const int MOD=1e9+7;
int pr[MAXN/8+5],prcnt=0,mnp[MAXN+5];
bitset<MAXN+5> vis;
void sieve(int n){
	for(int i=2;i<=n;i++){
		if(!vis[i]) pr[++prcnt]=i,mnp[i]=i;
		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
			vis[pr[j]*i]=1;mnp[pr[j]*i]=pr[j];
			if(i%pr[j]==0) break;
		}
	}
}
int n,a[MAXN+5],fac[MAXN+5],mul[MAXN+5],num[MAXN+5];
int to1[MAXN+5],to2[MAXN+5];
int c1[MAXN+5],c2[MAXN+5];
vector<int> pf[MAXN+5];
int main(){
	scanf("%d",&n);sieve(n);
	for(int i=2;i<=n;i++){
		int tmp=i;mul[i]=1;
		while(tmp^1){
			int p=mnp[tmp];
			while(tmp%p==0) tmp/=p;
			pf[i].pb(p);mul[i]*=p;
		} c1[mul[i]]++;
	} pf[1].pb(1);num[1]=1;c2[1]++;
	for(int i=1;i<=prcnt;i++) num[pr[i]]=n/pr[i],c2[num[pr[i]]]++;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) if(a[i]){
		if(pf[i].size()!=pf[a[i]].size()) return puts("0"),0;
		for(int j=0;j+1<pf[i].size();j++) if(pf[i][j]!=pf[i][j]) return puts("0"),0;
		int x=pf[i].back(),y=pf[a[i]].back();
		if(num[x]!=num[y]) return puts("0"),0;
		if(to1[x]&&to1[x]!=y) return puts("0"),0;
		if(to2[y]&&to2[y]!=x) return puts("0"),0;
		if(!to1[x]&&!to2[y]) c2[num[x]]--;
		to1[x]=y;to2[y]=x;c1[mul[a[i]]]--;
	} int res=1;
	for(int i=(fac[0]=1);i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;
	for(int i=1;i<=n;i++) res=1ll*res*fac[c1[i]]%MOD;
	for(int i=1;i<=n;i++) res=1ll*res*fac[c2[i]]%MOD;
	printf("%d\n",res);
	return 0;
}
这篇关于Codeforces 698F - Coprime Permutation(找性质)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!