CF698F Coprime Permutation

一、题目

点此看题

二、解法

网上大多数题解我都不满意,但是这里要强推 Qiuly 大佬的题解啊,讲得是真的好。虽然本题的关键步骤我已经走出来了,但是为什么我难以继续走下去?为什么我难以完整地想出一道题呢?

首先考虑怎么判定一个已知的排列是否合法,然后我自己想出了一个数链理论:我们选取每个质数作为基数,然后取出 \(1\sim n\) 中这个质数的所有倍数构成数链,那么数链内部的顺序可以"调整",数链之间不能互换。举个例子,若 \(n=10\),那么我认为 \(2,4,6,8,10\) 应该在位置 \(2,4,6,8,10\) 上,它们内部顺序可以互换。

上面的理论好像既不充分也不必要,我们考虑对它加以修正。考虑对于单独的数其实满足所有数链的限制就可以了,所以数交换的充要条件是两个数的质因数组合相同,所以我们可以统计 \(s_1[x]\) 表示质因数组合为 \(x\) 的数的出现次数,然后用阶乘就可以计算出方案数。

上面的思考还是不够全面,因为对于 \(>\sqrt n\) 的质数,可能出现 \(\lfloor\frac{n}{p}\rfloor\) 相同的情况,那么就说明可以交换整个数链。那么我们统计 \(s_2[x]\) 表示 \(\lfloor\frac{n}{p}\rfloor=x\) 的质数个数,这里也是用阶乘计算出方案数。

那么判断题目给出的部分排列是否合法的方法也就呼之欲出了,按照下面的步骤来吧:

  • 首先判断位置 \(i\) 和给定数 \(x\) 的质因数分解是否相同(除去最大质数),不同则无解。
  • 然后判断最大质数是否能交换数链。
  • 判断最大质数之间的映射关系,设 \(a[u]\) 表示 \(u\) 被谁替换了,\(b[u]\) 表示 \(u\) 替换了谁,维护它们两个即可。
  • 维护 \(s_1\)\(s_2\),方便最后计算方案数。

方法就这样,我又没做出来题,总之现在就是非常后悔,非常后悔。

#include <cstdio>
#include <vector>
#include <cstdlib>
using namespace std;
const int M = 1000005;
const int MOD = 1e9+7;
#define pb push_back
#define gg {puts("0");exit(0);}
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a[M],b[M],p[M],vis[M];vector<int> y[M];
int fac[M],num[M],siz[M],s0[M],s1[M];
/*
a[u] : the u is repalced by a[u]
b[u] : u replace b[u]
*/
void sieve()
{
	fac[0]=1;
	for(int i=1;i<=n;i++) num[i]=1;
	for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;
	for(int i=2;i<=n;i++) if(!vis[i])
	{
		p[++m]=i;num[i]=i;y[i].pb(i);
		for(int j=i+i;j<=n;j+=i)
			vis[j]=1,num[j]*=i,y[j].pb(i);
	}
	y[1].pb(1);siz[1]=1;
	for(int i=2;i<=n;i++) siz[i]=n/i;
	for(int i=1;i<=n;i++) s0[siz[i]]+=!vis[i],s1[num[i]]++;
}
signed main()
{
	n=read();sieve();
	for(int i=1;i<=n;i++)
	{
		int x=read();
		if(x==0) continue;
		if(y[i].size()!=y[x].size()) gg
		for(int j=0;j+1<y[i].size();j++)
			if(y[i][j]!=y[x][j]) gg
		int u=y[i].back(),v=y[x].back();
		if(siz[u]!=siz[v]) gg
		if(a[u] && a[u]!=v) gg
		if(b[v] && b[v]!=u) gg
		s0[siz[u]]-=!a[u];s1[num[x]]--;
		a[u]=v;b[v]=u;
	}
	int ans=1;
	for(int i=1;i<=n;i++)
		ans=1ll*ans*fac[s0[i]]%MOD*fac[s1[i]]%MOD;
	printf("%lld\n",ans);
}
posted @ 2022-01-14 22:09  C202044zxy  阅读(71)  评论(0编辑  收藏  举报