loj#2527 「HAOI2018」染色 容斥+NTT

版权声明:转吧转吧这条东西只是来搞笑的。。 https://blog.csdn.net/jpwang8/article/details/88982861

Description


为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可以抽象为一个长度为N 的序列, 每个位置都可以被染成M种颜色中的某一种.

然而小 C 只关心序列的N个位置中出现次数恰好为S 的颜色种数, 如果恰好出现了S 次的颜色有K 种, 则小C会产生Wk的愉悦度.

小 C 希望知道对于所有可能的染色方案, 他能获得的愉悦度的和对1004535809取模的结果是多少.
n 1 0 7 , m 1 0 5 n\le10^7,m\le10^5

Solution


观察发现出现次数的限制要强一点,容易想到设f(x)表示至少x个颜色出现了恰好s次,那么
f ( x ) = ( m x ) ( n x s ) ( x s ) ! ( s ! ) x ( m x ) n x s f(x)=\binom{m}{x}\binom{n}{xs}\frac{{\left(xs\right)}!}{{\left(s!\right)}^x}{{\left(m-x\right)}^{n-xs}}
于是套一个二项式反演就有
g ( k ) = i = k m ( i k ) ( 1 ) i k f ( i ) g(k)=\sum_{i=k}^{m}{\binom{i}{k}{\left(-1\right)}^{i-k}f(i)}
我们拆一下就可以翻转g构造卷积了,NTT直接做就行
注意那个n的范围。。一开始只开了1e6

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int MOD=1004535809;
const int N=2000005;

LL fac[N*5],inv[N*5],f[N],g[N],w[N];
int rv[N];

LL ksm(LL x,LL dep) {
	LL res=1;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

void NTT(LL *a,int n,int f) {
	for (int i=0;i<n;++i) if (i<rv[i]) std:: swap(a[i],a[rv[i]]);
	for (int i=1;i<n;i<<=1) {
		LL wn=ksm(3,(f==1)?((MOD-1)/i/2):(MOD-1-(MOD-1)/i/2));
		for (int j=0;j<n;j+=(i<<1)) {
			LL w=1;
			for (int k=0;k<i;++k) {
				LL u=a[j+k],v=a[j+k+i]*w%MOD;
				a[j+k]=(u+v)%MOD;
				a[j+k+i]=(u+MOD-v)%MOD;
				w=w*wn%MOD;
			}
		}
	}
	if (f==-1) {
		LL ny=ksm(n,MOD-2);
		for (int i=0;i<n;++i) a[i]=a[i]*ny%MOD;
	}
}

LL C(int n,int m) {
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}

int main(void) {
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	rep(i,2,N*5-1) {
		fac[i]=fac[i-1]*i%MOD;
		inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	}
	rep(i,2,N*5-1) inv[i]=inv[i-1]*inv[i]%MOD;
	int n,m,s; scanf("%d%d%d",&n,&m,&s);
	rep(i,0,m) scanf("%lld",&w[i]);
	rep(i,0,m) {
		if (s*i>n) break;
		f[i]=C(m,i)*C(n,s*i)%MOD*fac[s*i]%MOD*ksm(inv[s],i)%MOD*ksm(m-i,n-s*i)%MOD*fac[i]%MOD;
	}
	rep(i,0,m) {
		if ((m-i)&1) g[i]=MOD-inv[m-i];
		else g[i]=inv[m-i];
	}
	int lg=0,len=1; for (;len<=m*2;len<<=1,lg++);
	for (int i=0;i<len;++i) rv[i]=(rv[i>>1]>>1)|((i&1)<<(lg-1));
	NTT(f,len,1); NTT(g,len,1);
	for (int i=0;i<len;++i) f[i]=f[i]*g[i]%MOD;
	NTT(f,len,-1);
	LL ans=0;
	rep(i,0,m) {
		ans=(ans+f[i+m]*w[i]%MOD*inv[i]%MOD)%MOD;
	}
	printf("%lld\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/88982861