[HAOI2018]染色


题解

我竟然能自己想出来计数题

容斥+NTT
首先一看到题目让求恰好出现\(S\)次的颜色有恰好\(K\)种就想到容斥
如果容斥每种颜色出现次数的话并不好计数
那么考虑容斥恰好出现\(S\)次的颜色有几种
\(F(i)\)表示恰好出现\(S\)次的颜色有至少\(i\)
那么很显然\(F(i) = C(n , iS) * C(m , i) * \frac{(iS)!}{S!^i} * (m - i) ^ {n - iS}\)
表示从\(n\)个位置中选出\(iS\)个位置用来涂这\(i\)种颜色,然后从\(m\)种颜色中选择了\(i\)种颜色,在选出的\(iS\)个位置每种颜色都涂上\(S\)个的方案数再乘上选剩下的\(n-iS\)个位置随便的放选剩下的\(m-i\)种颜色
然后设\(f(k)\)表示恰好出现\(S\)次的颜色有恰好\(k\)
那么容斥就很显然了\(f(k)=\sum_{i=k}^{m}{(-1)^{i - k}C(i , k)F(i)}\)
这样复杂度就是\(O(n^2)\)
然后考虑把这个\(f(k)\)给化开
\(f(k)=\sum_{i=k}^{m}{(-1)^{i-k}C(i,k)C(n,iS)C(m,i)\frac{(iS)!}{S!^i}(m-i)^{n-iS}}\)
然后把组合数都化开
\(f(k)=\sum_{i=k}^{m}{(-1)^{i-k}\frac{i!}{k!(i-k)!}\frac{n!}{iS!(n-iS)!}\frac{m!}{i!(m-i)!}\frac{(iS)!}{S!^i}(m-i)^{n-iS}}\)
然后把能消的都消掉就成了\(f(k)=\frac{n!m!}{k!}\sum_{i=k}^{m}{\frac{(-1)^{i-k}}{(i-k)!}\frac{(m-i)^{n-iS}}{(n-iS)!(m-i)!S!^i}}\)
然后可以发现后面那些东西可以设为\(g(i)\)
前面那些东西可以设为\(t(i-k)\)
那么把\(g\)倒过来卷积即可

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 400005 ;
const int N = 10000005 ;
const int mod = 1004535809 ;
const int G = 3 ;
using namespace std ;

inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

int n , m , S , upp ;
int val[M] , fac[N] ;
int lim = 1 , len ;
LL a[M] , b[M] , r[M] , ans ;

inline int Fpw(int Base , int k) {
	int temp = 1 ; Base = (Base % mod + mod) % mod ;
	while(k) {
		if(k & 1) temp = 1LL * temp * Base % mod ;
		Base = 1LL * Base * Base % mod ; k >>= 1 ;
	}
	return temp ;
}
inline int inv(int x) {
	x = (x % mod + mod) % mod ;
	return Fpw(x , mod - 2) ;
}
inline void NTT(LL *A , int unit) {
	for(int i = 1 ; i <= lim ; i ++) if(r[i] > i) swap(A[i] , A[r[i]]) ;
	for(int mid = 1 ; mid < lim ; (mid <<= 1)) {
		LL R = (mid << 1) ; LL W = Fpw(unit > 0 ? G : inv(G) , (mod - 1) / R) ;
		for(int j = 0 ; j < lim ; j += R) {
			LL w = 1 ;
			for(int k = 0 ; k < mid ; k ++ , w = (w * W) % mod) {
				LL x = A[j + k] , y = w * A[j + k + mid] % mod ;
				A[j + k] = (x + y) % mod ; A[j + k + mid] = (x - y) % mod ;
			}
		}
	}
}
int main() {
	n = read() ; m = read() ; S = read() ;
	for(int i = 0 ; i <= m ; i ++) val[i] = read() ;
	if(S) upp = min(n / S , m) ; else upp = m ;
	fac[0] = 1 ; for(int i = 1 ; i <= max(n , m) ; i ++) fac[i] = 1LL * fac[i - 1] * i % mod ;
	for(int i = 0 ; i <= upp ; i ++) {
		a[i] = 1LL * Fpw(m - i , n - i * S) * inv(fac[n - i * S]) % mod * inv(fac[m - i]) % mod * inv( Fpw( fac[S] , i ) ) % mod ;
		b[i] = 1LL * Fpw(-1 , i) * inv(fac[i]) % mod ;
	}
	for(int i = 0 ; i <= upp / 2 ; i ++) swap(a[i] , a[upp - i]) ;
	while(lim <= upp * 2) (lim <<= 1) , ++ len ;
	for(int i = 1 ; i <= lim ; i ++) r[i] = ((r[i >> 1] >> 1) | ((i & 1) << (len - 1))) ;
	NTT(a , 1) ; NTT(b , 1) ;
	for(int i = 0 ; i <= lim ; i ++) a[i] = (a[i] * b[i] % mod + mod) % mod ;
	NTT(a , -1) ; LL tinv = inv(lim) ;
	for(int i = 0 ; i <= upp ; i ++) {
		LL temp = (a[upp - i] * tinv % mod + mod) % mod ;
		ans = (ans + 1LL * val[i] * temp % mod * fac[n] % mod * fac[m] % mod * inv(fac[i]) % mod) ;
		ans = (ans % mod + mod) % mod ;
	}
	printf("%lld\n",ans) ;
	return 0 ;
}
posted @ 2019-03-06 21:41  beretty  阅读(129)  评论(0编辑  收藏  举报