CF698F Coprime Permutation 题解

题意

给定一个未填满的数组 p,求有多少种 1n 的排列 p 满足对于任意 i<j,都有 [gcd(i,j)=1]=[gcd(pi,pj)=1],答案对 109+7 取模。

题解

部分参考这篇题解(感觉这篇题解应该是目前为止最详细的吧)。

P[1,n] 中所有素数与 1 构成的集合,gn 为所有在 n 中出现过的质因子的乘积(g1=1)。

由于 P 中任意两数 i,j 互质,故 (pi,pj)=1,从而 (gpi,gpj)=1

因此,记 G={gpi|iP},根据 [1,n] 中质因子的个数可分析出 GP 的一个排列。

进一步对于合数 xgx=iP,gcd(i,x)>1gi (1)

因此,如果 P 中数对应的 p 值确定了,那么所有数的 g 值也就都确定了。

先考虑所有 pi 都为 0 的情况。定义 Si={it|1itn,tZ},那么 |Si|=ni,于是对于质数 i,jpi=j 的一个条件为 |Si|=|Sj|ni=nj),同时可知 iji,j>n (2)

确定完所有数的 g 值后,如果对于 i,jgi=gj,那么 i,j 是等价的,于是 i,jp 中的位置可以任意交换。

根据上述推导,可以发现如果设 cnti 表示有多少个质数 p 满足 np=icnt2i 表示有多少个数的 g 值等于 i,那么答案即为 i=1n(cnti)!(cnt2i)!

如果位置 ipi 有值,首先要判断该位置上填的值是否合法。设 tigi 中不超过 n 的质因子的乘积,则 giti 等于 gi 的大于 n 的质因子或 1(没有大于 n 的质因子),不合法的情况有以下三种:

  1. titpi。注意由 (2) 可以推出对于所有不超过 n 的质数 pgp=p,所以 gigpi 不超过 n 的质因子应该相同。

  2. |Sgiti||Sgpitpi|。因为可以推出 gi 的不同质因子个数和 gpi 的不同质因子个数相同,并且大于 n 的质因子至多只有一个,所以要么 gigpi 同时没有大于 n 的质因子,要么同时有大于 n 的质因子、且根据 (1)(2) 可知 |Sgiti|=|Sgpitpi|

  3. P,G 无法构成一一映射,即存在不同的 i,jP 使得 [giti=gjtj]xor[gpitpi=gpjtpj]=1。前者为 1 后者为 0 是说 g 中存在某个位置 gk(其中 k>nkP),gk 要等于 2 个不同的值;前者为 0 后者为 1 是说 g 中存在某两个位置 gk,gl(其中 k,l>nk,lP),gk,gl 要等于 1 个相同的值。

如果合法,只需修改对应位置的 cntcnt2 的值即可。注意最后一步中 i=1pi=1 时要做一些特判,但大体思路相同。总时间复杂度 O(n)

Code

#include<bits/stdc++.h>
#define LL long long
#define mod 1000000007
using namespace std;
int n,p_t=0;
LL ans=1;
int a[1000002],p[1000002],t[1000002],t1[1000002],cnt[1000002],cnt2[1000002],to[1000002];
LL fac[1000002];
bool u[1000002];
inline void init()
{
	for(int i=fac[0]=cnt2[1]=1;i<=n;++i)t[i]=t1[i]=1,fac[i]=(fac[i-1]*i)%mod;
	t[1]=n;
	for(int i=2;i<=n;++i)
	{
		if(!u[i])
		{
			++cnt2[n/(p[++p_t]=i)];
			for(int j=i;j<=n;j+=i)t[j]*=i;
			if(i<=n/i)for(int j=i;j<=n;j+=i)t1[j]*=i;
		}
		for(int j=1;p[j]*i<=n;++j)
		{
			u[p[j]*i]=1;
			if(!(i%p[j]))break;
		}
	}
	for(int i=2;i<=n;++i)++cnt[t[i]];
}
int main()
{
	scanf("%d",&n),init();
	for(int i=1,x;i<=n;++i)
	{
		scanf("%d",&a[i]);
		if(a[i])
		{
			x=t[a[i]]/t1[a[i]];
			if((t1[a[i]]^t1[i]) || ((n/x)^(n/(t[i]/t1[i]))))return 0&puts("0");
			if(a[i]>1)--cnt[t[a[i]]];
			if(a[i]==1)--cnt2[1];
			else if(x>n/x)
			{
				if(!to[x])to[x]=t[i]/t1[i],--cnt2[n/x];
				else if(to[x]^(t[i]/t1[i]))return 0&puts("0");
			}
		}
	}
	for(int i=1;i<=n;++i)(((ans*=fac[cnt[i]])%=mod)*=fac[cnt2[i]])%=mod;
	return 0&printf("%lld",ans);
}
posted @   18Michael  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示