CF698F Coprime Permutation 题解

发布时间 2023-04-11 23:20:11作者: 18Michael

题意

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

题解

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

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

由于 \(P\) 中任意两数 \(i,j\) 互质,故 \((p_i,p_j)=1\),从而 \((g_{p_i},g_{p_j})=1\)

因此,记 \(G=\{g_{p_i}|i\in P\}\),根据 \([1,n]\) 中质因子的个数可分析出 \(G\)\(P\) 的一个排列。

进一步对于合数 \(x\)\(g_x=\prod_{i\in P,\gcd(i,x)>1}g_i\) \((1)\)

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

先考虑所有 \(p_i\) 都为 \(0\) 的情况。定义 \(S_i=\{it|1\le it\le n,t\in Z\}\),那么 \(|S_i|=\lfloor\frac{n}{i}\rfloor\),于是对于质数 \(i,j\)\(p_i=j\) 的一个条件为 \(|S_i|=|S_j|\)\(\lfloor\frac{n}{i}\rfloor=\lfloor\frac{n}{j}\rfloor\)),同时可知 \(i\ne j\)\(i,j\gt\sqrt n\) \((2)\)

确定完所有数的 \(g\) 值后,如果对于 \(i,j\)\(g_{i}=g_{j}\),那么 \(i,j\) 是等价的,于是 \(i,j\)\(p\) 中的位置可以任意交换。

根据上述推导,可以发现如果设 \(cnt_i\) 表示有多少个质数 \(p\) 满足 \(\lfloor\frac{n}{p}\rfloor=i\)\(cnt2_i\) 表示有多少个数的 \(g\) 值等于 \(i\),那么答案即为 \(\prod_{i=1}^n(cnt_i)!(cnt2_i)!\)

如果位置 \(i\)\(p_i\) 有值,首先要判断该位置上填的值是否合法。设 \(t_{i}\)\(g_i\) 中不超过 \(\sqrt n\) 的质因子的乘积,则 \(\frac{g_i}{t_i}\) 等于 \(g_i\) 的大于 \(\sqrt n\) 的质因子或 \(1\)(没有大于 \(\sqrt n\) 的质因子),不合法的情况有以下三种:

  1. \(t_{i}\ne t_{p_i}\)。注意由 \((2)\) 可以推出对于所有不超过 \(\sqrt n\) 的质数 \(p\)\(g_p=p\),所以 \(g_i\)\(g_{p_i}\) 不超过 \(\sqrt n\) 的质因子应该相同。

  2. \(|S_{\frac{g_i}{t_i}}|\ne|S_{\frac{g_{p_i}}{t_{p_i}}}|\)。因为可以推出 \(g_i\) 的不同质因子个数和 \(g_{p_i}\) 的不同质因子个数相同,并且大于 \(\sqrt n\) 的质因子至多只有一个,所以要么 \(g_i\)\(g_{p_i}\) 同时没有大于 \(\sqrt n\) 的质因子,要么同时有大于 \(\sqrt n\) 的质因子、且根据 \((1)(2)\) 可知 \(|S_{\frac{g_i}{t_i}}|=|S_{\frac{g_{p_i}}{t_{p_i}}}|\)

  3. \(P,G\) 无法构成一一映射,即存在不同的 \(i,j\in P\) 使得 \(\left[\frac{g_i}{t_i}=\frac{g_j}{t_j}\right]xor\left[\frac{g_{p_i}}{t_{p_i}}=\frac{g_{p_j}}{t_{p_j}}\right]=1\)。前者为 \(1\) 后者为 \(0\) 是说 \(g\) 中存在某个位置 \(g_k\)(其中 \(k\gt\sqrt n\)\(k\in P\)),\(g_k\) 要等于 \(2\) 个不同的值;前者为 \(0\) 后者为 \(1\) 是说 \(g\) 中存在某两个位置 \(g_k,g_l\)(其中 \(k,l\gt\sqrt n\)\(k,l\in P\)),\(g_k,g_l\) 要等于 \(1\) 个相同的值。

如果合法,只需修改对应位置的 \(cnt\)\(cnt2\) 的值即可。注意最后一步中 \(i=1\)\(p_i=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);
}