背景:
暑假集训七月份最后一天,写写原来没有写过的
blog
\text{blog}
blog吧。
第
300
300
300篇原创。
题目传送门:
https://www.luogu.org/problemnew/show/P4491
题意:
给出
n
,
m
,
s
,
W
k
n,m,s,W_k
n,m,s,Wk,现在有一个长度为
n
n
n的序列和
m
m
m种颜色,可以给这个序列染色,若出现
S
S
S次的颜色有
k
k
k种,则会产生
W
k
W_k
Wk的贡献,问最后的贡献。
思路:
假设出现
S
S
S次的颜色有
k
k
k种。
设
f
k
f_k
fk表示至少有
k
k
k种出现
S
S
S次的颜色的方案数,则有:
f
k
=
C
m
k
C
n
k
s
(
k
s
)
!
(
s
!
)
k
(
m
−
k
)
n
−
k
s
f_k=\frac{C_{m}^{k}C_{n}^{ks}(ks)!}{(s!)^k}(m-k)^{n-ks}
fk=(s!)kCmkCnks(ks)!(m−k)n−ks
解释一下:
在
m
m
m种颜色中选
k
k
k种的方案数为
C
m
k
C_{m}^{k}
Cmk;
在
n
n
n个位置中选
k
s
ks
ks个的方案数为
C
n
k
s
C_{n}^{ks}
Cnks;
在这
k
s
ks
ks个位置中,可以打乱排序,方案数为
(
k
s
)
!
(ks)!
(ks)!,又因为交换相同的颜色方案数不变,因此要除以
(
s
!
)
k
(s!)^k
(s!)k;
在剩下的
n
−
k
s
n-ks
n−ks的位置上可以填
m
−
k
m-k
m−k种颜色,方案数为
(
m
−
k
)
n
−
k
s
(m-k)^{n-ks}
(m−k)n−ks。
设
g
i
g_i
gi表示出现
S
S
S次的颜色有
i
i
i种的方案数。
仔细思考,发现有:
f
k
=
∑
i
=
k
m
C
i
k
g
i
f_k=\sum_{i=k}^{m}C_{i}^{k}g_i
fk=i=k∑mCikgi
二项式反演一下,有:
g
k
=
∑
i
=
k
m
(
−
1
)
i
−
k
C
i
k
f
i
g_k=\sum_{i=k}^{m}(-1)^{i-k}C_{i}^{k}f_i
gk=i=k∑m(−1)i−kCikfi
化简一下,有:
g
k
=
∑
i
=
k
m
(
−
1
)
i
−
k
i
!
f
i
k
!
(
i
−
k
)
!
g_k=\sum_{i=k}^{m}(-1)^{i-k}\frac{i!f_i}{k!(i-k)!}
gk=i=k∑m(−1)i−kk!(i−k)!i!fi
g k = 1 k ! ∑ i = k m i ! f i ( − 1 ) i − k ( i − k ) ! g_k=\frac{1}{k!}\sum_{i=k}^{m}i!f_i\frac{(-1)^{i-k}}{(i-k)!} gk=k!1i=k∑mi!fi(i−k)!(−1)i−k
发现里面是一个卷积的形式,用
NTT
\text{NTT}
NTT优化即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const int mod=1004535809,G=3,inv_G=334845270;
using namespace std;
int fac[12000010],Inv[12000010],a[12000010],b[12000010],f[12000010],g[12000010],w[12000010];
int limit,l,r[12000010];
int n,m,s,ans=0;
int dg(int x,int k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
LL C(int n,int m)
{
return (LL)fac[n]*Inv[m]%mod*Inv[n-m]%mod;
}
void INIT()
{
fac[0]=fac[1]=1;
Inv[0]=Inv[1]=1;
for(int i=2;i<=max(n,m);i++)
{
fac[i]=(LL)fac[i-1]*i%mod;
Inv[i]=((LL)mod-mod/i)*Inv[mod%i]%mod;
}
for(int i=2;i<=max(n,m);i++)
Inv[i]=(LL)Inv[i]*Inv[i-1]%mod;
for(int k=0;k<=min(n/s,m);k++)
f[k]=(LL)C(m,k)*C(n,k*s)%mod*fac[k*s]%mod*dg(Inv[s],k)%mod*dg(m-k,n-k*s)%mod;
}
int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=0;i<=n;i++)
scanf("%d",&w[i]);
INIT();
init(m+1);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<=m;i++)
{
a[i]=(LL)f[i]*fac[i]%mod;
b[i]=((((m-i)&1)?-1ll:1ll)*Inv[m-i]%mod+mod)%mod;
}
NTT(a,limit,1),NTT(b,limit,1);
for(int i=0;i<limit;i++)
a[i]=(LL)a[i]*b[i]%mod;
NTT(a,limit,-1);
int INV=inv(limit);
for(int i=0;i<limit;i++)
f[i]=(LL)a[i]*INV%mod;
for(int i=0;i<=m;i++)
ans=((LL)ans+(LL)f[i+m]*Inv[i]%mod*w[i]%mod)%mod;
printf("%d",ans);
}