0%

CF840C On the Bench

注意到若 \(a_i=c^2r\),则让 \(a_i:=r\) 答案不会变。

则现在将问题转化为有 \(n\) 个有标号的不同颜色的球,问有多少种排列使得没有同色球相邻,设 \(a_i\) 为颜色为 \(i\) 的球的个数。

我们考虑容斥,我们注意到没有同色球相邻等价于 \(n-1\) 个限制:\(\forall i=1\rightarrow n-1\)\(i\)\(i+1\) 上的球的颜色不同 。设 \(f(i)\) 表示恰好有 \(i\) 个限制未被满足的方案数,\(g(i)\) 为钦定 \(i\) 个位置的限制未被满足的方案数,注意到这是一个子集反演的形式(把每个位置的限制是否被满足看作集合的元素)(update:经过讨论这似乎是二项式反演,只不过二项式反演和子集反演在求 \(f(0)\) 时是一致的),则 \(f(0)=\sum\limits_{i=0}^{n-1}(-1)^ig(i)\)

考虑求 \(g(i)\),我们考虑每种颜色球的贡献。与整体的情况类似,我们认为每一种球也有 \(a_k-1\) 个限制:最终排列完之后第 \(i\) 个球和第 \(i+1\) 个球不能相邻。

注意到如果我们钦定了某个位置的限制不满足,则会把两个球缩到一起。如果钦定有 \(i\) 个限制不满足,最后相当于把这种颜色的球缩成了 \(a_k-i\) 段,由于最后的拼接是关于球的段数的,所以我们写出关于最后剩余球的段数的 EGF: \[ F_k(z)=a_k!\sum\limits_{i=1}^{a_k}\binom{a_k-1}{i-1}\frac{z^i}{i!} \] 即我们先枚举一个标号的排列,然后选出 \(i-1\) 个位置切断,然后由于 EGF 要把新的段数的标号消掉所以要除以 \(i!\)

所以有 \(g(i)=(n-i)![z^{n-i}]\prod F_k(z)\)

由于这题模数是 \(10^9+7\) 而且 \(n\) 比较小,所以用了暴力卷积,否则可以使用分治+NTT 优化至 \(O(n\log^2n)\)

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<bits/stdc++.h>
using namespace std;
int const p=1e9+7;
int a[305],b[305],fac[305],inv[305];
vector<int>v[305];
bool visited[305];
int pw(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=1ll*res*x%p;
x=1ll*x*x%p;
y>>=1;
}
return res;
}
int main()
{
int n,m=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=1ll*fac[i-1]*i%p,inv[i]=1ll*inv[i-1]*pw(i,p-2)%p;
if(visited[i])continue;
m++;b[m]=1;
for(int j=i+1;j<=n;j++)
{
int t=(int)(sqrt(((long double)a[i])*a[j])+1e-12);
if(1ll*t*t==1ll*a[i]*a[j])visited[j]=1,b[m]++;
}
}
vector<int>res(1,1);
for(int i=1;i<=m;i++)
{
v[i].resize(b[i]+1);
for(int j=1;j<=b[i];j++)v[i][j]=1ll*fac[b[i]-1]*inv[j-1]%p*inv[b[i]-j]%p*inv[j]%p;
vector<int>tmp(res.size()+v[i].size()-1);
for(int j=1;j<=b[i];j++)
for(int k=0;k<res.size();k++)
tmp[j+k]=(tmp[j+k]+1ll*v[i][j]*res[k])%p;
res=tmp;
}
int ans=0;
for(int i=0;i<res.size();i++)
ans=(ans+1ll*((i&1)?p-1:1)*fac[n-i]%p*res[n-i])%p;
for(int i=1;i<=m;i++)ans=1ll*ans*fac[b[i]]%p;
printf("%d",ans);
return 0;
}