problem
群里有 k k k 个不同的复读机。为了庆祝平安夜的到来,在接下来的 n n n 秒内,它们每秒钟都会选出一位优秀的复读机进行复读。非常滑稽的是,一个复读机只有总共复读了 d d d 的倍数次才会感到快乐。问有多少种不同的安排方式使得所有的复读机都感到快乐。
答案对 19491001 19491001 19491001 取模。
数据范围:
n
≤
1
0
9
n\le10^9
n≤109。
当
d
=
1
,
2
d=1,2
d=1,2 时,
k
≤
5
×
1
0
5
k\le 5\times 10^5
k≤5×105。
当
d
=
3
d=3
d=3 时,
k
≤
1000
k\le 1000
k≤1000。
solution
这个数据范围提示我们应该要分类讨论。
当 d = 1 d=1 d=1 时,答案显然是 k n k^n kn。
当 d = 2 d=2 d=2 时,我们先把每个复读机的生成函数构造出来:
f ( x ) = ∑ i = 0 ∞ [ 2 ∣ i ] i ! x i = e x + e − x 2 f(x)=\sum_{i=0}^{\infty}\frac{[2|i]}{i!}x^i=\frac{e^x+e^{-x}}{2} f(x)=i=0∑∞i![2∣i]xi=2ex+e−x
PS:这个根据 e x = ∑ i = 0 ∞ x i i ! e^x=\sum\limits_{i=0}^{\infty}\frac{x^i}{i!} ex=i=0∑∞i!xi 就可以推出来。
那么答案应该是 [ x n ] f k ( x ) [x^n]f^k(x) [xn]fk(x),我们把 f k ( x ) f^k(x) fk(x) 展开:
f k ( x ) = 1 2 k ( e x + e − x ) k = 1 2 l ∑ i = 0 k ( k i ) e i x e ( k − i ) ( − x ) = 1 2 k ∑ i = 0 k ( k i ) e ( 2 i − k ) x \begin{aligned} f^k(x)&=\frac 1{2^k}(e^x+e^{-x})^k\\ &=\frac 1 {2^l}\sum_{i=0}^k\binom k i e^{ix}e^{(k-i)(-x)}\\ &=\frac 1 {2^k}\sum_{i=0}^k\binom k i e^{(2i-k)x} \end{aligned} fk(x)=2k1(ex+e−x)k=2l1i=0∑k(ik)eixe(k−i)(−x)=2k1i=0∑k(ik)e(2i−k)x
虽然 [ x n ] e a x [x^n]e^{ax} [xn]eax 根据泰勒展开应该是 a n n ! \frac{a^n}{n!} n!an,但是由于 EGF 算出答案后会乘个 n ! n! n!,所以:
a n s = 1 2 k ∑ i = 0 k ( k i ) ( 2 i − k ) n ans=\frac 1 {2^k}\sum_{i=0}^k\binom k i (2i-k)^n ans=2k1i=0∑k(ik)(2i−k)n
那么这一部分我们就可以 O ( k log n ) O(k\log n) O(klogn) 解决掉啦。
当 d = 3 d=3 d=3 时。
还是先把生成函数写出来吧(其中 d = 3 d=3 d=3):
f ( x ) = ∑ i = 0 ∞ [ d ∣ i ] x i i ! f(x)=\sum_{i=0}^{\infty}[d|i]\frac{x^i}{i!} f(x)=i=0∑∞[d∣i]i!xi
看到那个 [ d ∣ i ] [d|i] [d∣i] 有点不爽,想办法把它换掉。
考虑单位根反演,即:
[ d ∣ k ] = ∑ i = 0 d − 1 ω d i k d [d|k]=\frac{\sum_{i=0}^{d-1}\omega_d^{ik}}{d} [d∣k]=d∑i=0d−1ωdik
简单证明如下:
- 当 d ∣ k d|k d∣k 时,根据消去引理,有 ω d i k = ω 1 k ′ = 1 \omega_d^{ik}=\omega_1^{k'}=1 ωdik=ω1k′=1,那么 ∑ i = 0 d − 1 ω d i k d = 1 = \frac{\sum_{i=0}^{d-1}\omega_d^{ik}}{d}=1= d∑i=0d−1ωdik=1= 左边。
- 当 d ∣ ̸ k d|\not k d∤k 时,右边相当于是等比数列求和,为 1 d × 1 − ( ω d k ) d 1 − ω d k \frac 1 d\times\frac{1-(\omega_d^k)^d}{1-\omega_d^k} d1×1−ωdk1−(ωdk)d,由于 1 − ( ω d k ) d = 0 1-(\omega_d^k)^d=0 1−(ωdk)d=0,所以右边 = 0 = =0= =0= 左边。
证毕。
那么代进去,得到:
f ( x ) = 1 d ∑ i = 0 ∞ ∑ j = 0 d − 1 ( ω d j x ) i i ! = 1 d ∑ j = 0 d − 1 e ω d j x \begin{aligned} f(x)&=\frac 1 d\sum_{i=0}^{\infty}\sum_{j=0}^{d-1}\frac{(\omega_d^jx)^i}{i!}\\ &=\frac 1 d\sum_{j=0}^{d-1}e^{\omega_d^jx} \end{aligned} f(x)=d1i=0∑∞j=0∑d−1i!(ωdjx)i=d1j=0∑d−1eωdjx
也就是说,这时的 f ( x ) = 1 3 ( e x + e ω 3 1 x + e ω 3 2 x ) f(x)=\frac 1 3(e^x+e^{\omega_3^1x}+e^{\omega_3^2x}) f(x)=31(ex+eω31x+eω32x)。
我们把 f k ( x ) f^k(x) fk(x) 暴力展开(就是二项式定理套个二项式定理),得到:
f k ( x ) = 1 3 k ∑ i = 0 k ( k i ) ( ∑ j = 0 k − i ( k − i j ) e i x + ω 3 1 j x + ω 3 2 ( k − i − j ) x ) f^k(x)=\frac 1 {3^k}\sum_{i=0}^k\binom k i\left(\sum_{j=0}^{k-i}\binom{k-i} je^{ix+\omega_3^1jx+\omega_3^2(k-i-j)x}\right) fk(x)=3k1i=0∑k(ik)(j=0∑k−i(jk−i)eix+ω31jx+ω32(k−i−j)x)
时间复杂度 O ( k 2 ) O(k^2) O(k2)。
Tip: 处理 ω 3 1 \omega_3^1 ω31 和 ω 3 2 \omega_3^2 ω32。
const int P=19491001,g=7; int w1=power(g,(P-1)/3),w2=mul(w1,w1);
注意要满足 3 ∣ ( P − 1 ) 3|(P-1) 3∣(P−1)。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define P 19491001
using namespace std;
int n,k,d,fac[N],ifac[N];
const int inv2=9745501,inv3=12994001;
const int g=7,w1=18827933,w2=663067;
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y%P;}
int C(int n,int m) {return mul(fac[n],mul(ifac[m],ifac[n-m]));}
int power(int a,int b,int ans=1){
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
void init_fac(){
fac[0]=fac[1]=1;
for(int i=2;i<N;++i) fac[i]=mul(fac[i-1],i);
ifac[N-1]=power(fac[N-1],P-2);
for(int i=N-2;~i;--i) ifac[i]=mul(ifac[i+1],i+1);
}
int main(){
init_fac();
scanf("%d%d%d",&n,&k,&d);
if(d==1) printf("%d\n",power(k,n));
else if(d==2){
int ans=0;
for(int i=0;i<=k;++i) ans=add(ans,mul(C(k,i),power(dec(add(i,i),k),n)));
printf("%d\n",mul(ans,power(inv2,k)));
}
else{
int ans=0;
for(int i=0;i<=k;++i){
int temp,sum=0;
for(int j=0;j<=k-i;++j){
temp=add(i,add(mul(w1,j),mul(w2,k-i-j)));
sum=add(sum,mul(C(k-i,j),power(temp,n)));
}
ans=add(ans,mul(C(k,i),sum));
}
ans=mul(ans,power(inv3,k));
printf("%d\n",ans);
}
return 0;
}