zoukankan      html  css  js  c++  java
  • HDU 6116 路径计数

    HDU 6116 路径计数

    普通生成函数常用于处理组合问题,指数生成函数常用于处理排列问题。

    考虑 对于 $ a $ 个 $ A $ 分为很多堆,这么分的方案数是 $ C_{a-1}^{i-1} $

    然后对于每一堆我们看成一个数来放,并且所有堆都这样做,这样的话总的方案数量是 $ frac{(i+j+k+l)!}{i!j!k!l!} $

    就算所有一堆看成的数的排列是不存在相邻相等的,至少都有 $ n-i-j-k-l $ 对相邻的相同的数。

    然后就可以容斥了,枚举 $ i+j+k+l $ 直接计算就好了。

    $ ans = displaystyle sum_{x=1}^{n} (-1)^{n-x} x! sum_{i+j+k+l=x} frac{C_{a-1}^{i-1} C_{b-1}^{j-1} C_{c-1}^{k-1} C_{d-1}^{l-1}}{i ! j ! k ! l !} $

    后面其实就是四个指数生成函数乘积了。

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 200010
    #define P 998244353
    #define clr( a ) memset( a , 0 , sizeof a )
    typedef long long ll;
    int wn[2][MAXN];
    int Pow( int x , int y ) {
    	int res=1;
    	while(y) {
    		if(y&1) res=res*(ll)x%P;
    		x=x*(ll)x%P,y>>=1;
    	}
    	return res;
    }
    void getwn(int l) {
    	for(int i=1;i<(1<<l);i<<=1) {
    		int w0=Pow(3,(P-1)/(i<<1)),w1=Pow(3,P-1-(P-1)/(i<<1));
    		wn[0][i]=wn[1][i]=1;
    		for(int j=1;j<i;++j)
    			wn[0][i+j]=wn[0][i+j-1]*(ll)w0%P,
    			wn[1][i+j]=wn[1][i+j-1]*(ll)w1%P;
    	}
    }
    int rev[MAXN];
    void getr(int l) { for(int i=1;i<(1<<l);++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1); }
    void NTT(int *A,int len,int f) {
    	for(int i=0;i<len;++i) if(rev[i]<i) swap(A[i],A[rev[i]]);
    	for(int l=1;l<len;l<<=1)
    		for(int i=0;i<len;i+=(l<<1))
    			for(int k=0;k<l;++k) {
    				int t1=A[i+k],t2=A[i+l+k]*(ll)wn[f][l+k]%P;
    				A[i+k]=(t1+t2)%P;
    				A[i+l+k]=(t1-t2+P)%P;
    			}
    	if( f == 1 ) for(int inv=Pow(len,P-2),i=0;i<len;++i) A[i]=A[i]*(ll)inv%P;
    }
    int a , b , c , d;
    int J[MAXN] , invJ[MAXN] , inv[MAXN];
    int cc( int a , int b ) {
    	if( b > a ) return 0;
    	return 1ll * J[a] * invJ[b] % P * invJ[a - b] % P;
    }
    int A[MAXN] , B[MAXN] , C[MAXN] , D[MAXN];
    int main() {
    	J[0] = inv[1] = invJ[0] = J[1] = invJ[1] = 1; 
    	for( int i = 2 ; i < MAXN ; ++ i ) inv[i] = 1ll * ( P - P / i ) * inv[P % i] % P , J[i] = 1ll * J[i - 1] * i % P , invJ[i] = 1ll * invJ[i - 1] * inv[i] % P;
    	while( cin >> a >> b >> c >> d ) {
    		clr( A ) , clr( B ) , clr( C ) , clr( D );
    		int n = a + b + c + d;
    		for( int i = 1 ; i <= a ; ++ i ) A[i] = 1ll * cc( a - 1 , i - 1 ) * invJ[i] % P;
    		for( int i = 1 ; i <= b ; ++ i ) B[i] = 1ll * cc( b - 1 , i - 1 ) * invJ[i] % P;
    		for( int i = 1 ; i <= c ; ++ i ) C[i] = 1ll * cc( c - 1 , i - 1 ) * invJ[i] % P;
    		for( int i = 1 ; i <= d ; ++ i ) D[i] = 1ll * cc( d - 1 , i - 1 ) * invJ[i] % P;
    		int len = 1 , l = 0;
    		while( len <= n ) len <<= 1 , ++ l;
    		getwn( l ) , getr( l );
    		NTT( A , len , 0 ) , NTT( B , len , 0 ) , NTT( C , len , 0 ) , NTT( D , len , 0 );
    		for( int i = 0 ; i < len ; ++ i ) A[i] = 1ll * A[i] * B[i] % P * C[i] % P * D[i] % P;
    		NTT( A , len , 1 );
    		ll res = 0;
    		for( int i = 1 ; i <= n ; ++ i )
    			res += ( ( n - i & 1 ) ? -1ll : 1ll ) * J[i] * A[i] % P , res += P , res %= P;
    		cout << res << endl;
    	}
    }
    
  • 相关阅读:
    {转}每次从vss获取文件都是只读
    點擊按鈕后彈出新頁面導致原頁面CSS失效
    Mschat控件示例升级错误处理方法
    一个简单的使用EVP框架的加密过程 aes 128 ecb
    centos7的syslog知识点
    pam模块使用syslog日志调试
    Linux系统上的popen()库函数
    linux C语言 用openssl进行签名验签 --- 亲测2 sha256 sha512
    linux C语言 用openssl进行签名验签 --- 亲测 sha256 sha512
    k8s简介
  • 原文地址:https://www.cnblogs.com/yijan/p/hdu6116.html
Copyright © 2011-2022 走看看