集训队作业2018: 复读机(生成函数)

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

题解:
挺妙的,一个人的生成函数是 i = 0 [ d i ] i ! x i \sum_{i=0}^{\infty}\frac{[d|i]}{i!}x^i

这个 [ d i ] [d|i] 跟FFT中那个idft挺像的,找个 d d 次单位根,变成 j = 0 d 1 w d i j \sum_{j=0}^{d-1}w_{d}^{ij}

然后会发现 d = 2 d=2 时就是求 ( e x + e x 2 ) k (\frac{e^x+e^{-x}}{2})^k 的第 n n 项, d = 3 d=3 是求 ( e x + e r x + e r 2 x 3 ) k (\frac{e^x+e^{rx}+e^{r^2x}}{3})^k 的第 n n 项( r r 为三次单位根,可以用原根 g g g m o d 1 3 g^{\frac{mod-1}{3}} 来表示,此题原根为7)。

k k 较小,直接暴力二项式展开就可以过了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N=5e5+50;
const int mod=19491001;
const int r=18827933;

inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (LL)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
inline int cinv(int a) {return power(a,mod-2);}

struct combin {
	int fac[N],ifac[N];
	combin() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[N-1]=cinv(fac[N-1]);
		for(int i=N-2;~i;i--) ifac[i]=mul(ifac[i+1],i+1);
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
} C;

int n,k,d;

int main() {
	cin>>n>>k>>d;
	if(d==1) return cout<<power(k,n),0;
	else if(d==2) {
		int ans=0;
		for(int i=0;i<=k;i++) 
			ans=add(ans,mul(C.C(k,i),power(2*i-k,n)));
		ans=mul(ans,cinv(power(2,k)));
		return cout<<ans,0;
	} else {
		int ans=0;
		for(int i=0;i<=k;i++) {
			int sum=0;
			for(int j=0;j<=k-i;j++)
				sum=add(sum,mul(C.C(k-i,j),power(((LL)r*r*i%mod+(LL)r*j%mod+k-i-j)%mod,n)));
			ans=add(ans,mul(sum,C.C(k,i)));
		}
		ans=mul(ans,cinv(power(3,k)));
		return cout<<ans,0;
	}
}
发布了553 篇原创文章 · 获赞 227 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/qq_35649707/article/details/86541734