zoukankan      html  css  js  c++  java
  • [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
    ",ans) ;
    	return 0 ;
    }
    
  • 相关阅读:
    Proj THUDBFuzz Paper Reading: PMFuzz: Test Case Generation for Persistent Memory Programs
    入围 WF 后训练记
    算法竞赛历程
    2021 多校 杭电 第十场
    2021 多校 杭电 第九场
    2021 多校 牛客 第十场
    2021 多校 牛客 第九场
    2021 多校 杭电 第八场
    2021 多校 杭电 第六场
    2021 多校 杭电 第七场
  • 原文地址:https://www.cnblogs.com/beretty/p/10486314.html
Copyright © 2011-2022 走看看