【集训队作业2018】复读机 题解
【集训队作业2018】复读机 题解
题意
求有多少个长为 n,值域为 [1,k] 的序列满足 i=1∑k[d∣cnti]=k,其中 cnti 表示 i 在序列中出现的次数,答案对 19491001 取模。
n≤109,k≤5×105,d∈{1,2,3},若 d=3 则保证 k≤103。
d=1
大家都会!答案是 kn。
d=2
写出每一种颜色的 EGF:F(x)=i≥0∑[d∣i]i!xi,那么 ans=n![xn]Fk(x)。
这个 [d∣i] 看起来很不爽,让我们找不到封闭形式,直接把他单位根反演掉:
F(x)=i≥0∑d1j=0∑d−1ωdiji!xi=d1i=0∑d−1j≥0∑j!(ωdix)j=d1i=0∑d−1eωdix
这样就有封闭形式了,我们直接把 d=2 的时候的单位根代入:
ans=n![xn](dex+e−x)k=dkn![xn]i=0∑k(ik)eixe−(k−i)x
=dkn![xn]i=0∑k(ik)e(2i−k)x=dkn![xn]i=0∑k(ik)j≥0∑j!((2i−k)x)j
=dkn!i=0∑k(ik)n!(2i−k)n=dk1i=0∑k(ik)(2i−k)n
枚举 i,时间复杂度 O(klogn)。
d=3
我们延续 d=2 的思路,把 d=3 时的单位根代入:
ans=n![xn](dex+eω31x+eω32x)k=dkn![xn]i=0∑k(ik)j=0∑k−i(jk−i)eixejω31xe(k−i−j)ω32x
=dkn![xn]i=0∑k(ik)j=0∑k−i(jk−i)e(i+jω31+(k−i−j)ω32)x=dkn![xn]i=0∑k(ik)j=0∑k−i(jk−i)p≥0∑p!((i+jω31+(k−i−j)ω32)x)p
=dkn!i=0∑k(ik)j=0∑k−i(jk−i)n!(i+jω31+(k−i−j)ω32)n=dk1i=0∑k(ik)j=0∑k−i(jk−i)(i+jω31+(k−i−j)ω32)n
枚举 i,j,时间复杂度 O(k2logn)。
代码
冷知识:19491001 的原根是 7。
#include<iostream>
#include<cstdio>
using namespace std;
const int mod=19491001,g=7;
int n,k,d,fac[500001],inv[500001],w[3],ans;
inline int pw(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=1ll*res*a%mod;
b>>=1;
a=1ll*a*a%mod;
}
return res;
}
inline int c(int x,int y)
{
if(x<y||x<0||x<0)
return 0;
return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
cin>>n>>k>>d;
if(d==1)
{
cout<<pw(k,n)<<'\n';
return 0;
}
fac[0]=inv[0]=1;
for(int i=1;i<=k;++i)
fac[i]=1ll*fac[i-1]*i%mod;
inv[k]=pw(fac[k],mod-2);
for(int i=k-1;i>=1;--i)
inv[i]=1ll*inv[i+1]*(i+1)%mod;
if(d==2)
{
for(int i=0;i<=k;++i)
ans=(ans+1ll*c(k,i)*pw((2*i-k+mod)%mod,n)%mod)%mod;
cout<<1ll*ans*pw(pw(2,k),mod-2)%mod<<'\n';
return 0;
}
w[0]=1;
w[1]=pw(g,(mod-1)/3);
w[2]=1ll*w[1]*w[1]%mod;
for(int i=0;i<=k;++i)
{
int res=0;
for(int j=0;j<=k-i;++j)
res=(res+1ll*inv[j]*inv[k-i-j]%mod*pw((i+1ll*w[1]*j%mod+1ll*w[2]*(k-i-j)%mod)%mod,n)%mod)%mod;
ans=(ans+1ll*inv[i]*res%mod)%mod;
}
cout<<1ll*fac[k]*pw(pw(3,k),mod-2)%mod*ans%mod<<'\n';
return 0;
}
怄火。挥手。转圈。街舞。跳跳。献吻。跳绳。激动。发抖。磕头。爱情。飞吻。左太极。右太极。回头。