【UOJ 450】复读机

传送门


problem

群里有 k k k 个不同的复读机。为了庆祝平安夜的到来,在接下来的 n n n 秒内,它们每秒钟都会选出一位优秀的复读机进行复读。非常滑稽的是,一个复读机只有总共复读了 d d d 的倍数次才会感到快乐。问有多少种不同的安排方式使得所有的复读机都感到快乐。

答案对 19491001 19491001 19491001 取模。

数据范围: n ≤ 1 0 9 n\le10^9 n109
d = 1 , 2 d=1,2 d=1,2 时, k ≤ 5 × 1 0 5 k\le 5\times 10^5 k5×105
d = 3 d=3 d=3 时, k ≤ 1000 k\le 1000 k1000


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=0i![2i]xi=2ex+ex

PS:这个根据 e x = ∑ i = 0 ∞ x i i ! e^x=\sum\limits_{i=0}^{\infty}\frac{x^i}{i!} ex=i=0i!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+ex)k=2l1i=0k(ik)eixe(ki)(x)=2k1i=0k(ik)e(2ik)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=0k(ik)(2ik)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[di]i!xi

看到那个 [ d ∣ i ] [d|i] [di] 有点不爽,想办法把它换掉。

考虑单位根反演,即:

[ d ∣ k ] = ∑ i = 0 d − 1 ω d i k d [d|k]=\frac{\sum_{i=0}^{d-1}\omega_d^{ik}}{d} [dk]=di=0d1ωdik

简单证明如下:

  • d ∣ k d|k dk 时,根据消去引理,有 ω 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= di=0d1ω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=0j=0d1i!(ωdjx)i=d1j=0d1eω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=0k(ik)(j=0ki(jki)eix+ω31jx+ω32(kij)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(P1)


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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值