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);
}