zoukankan      html  css  js  c++  java
  • Comet OJ Round11E ffort

    题面描述

    给定 (n) 种变量,每种变量有 (a_i) 个,每个变量的取值为 ([1,b_i]),对于每个方案,记 (S) 为其和,然后将 (S) 分配给 (m) 个初始为 (0) 的变量,需要保证每一个都大于 (0),求方案数。答案对 (998244353) 取模。

    (n imes mle 10^5,a_ile 10^5,b_iin [1,998244352])

    ( m Sol:)

    大概是兔队的做法,这个题太仙了,我直接膜拜兔队。

    对于某个 (S),答案分成两个部分,第一部分是得到 (S) 的方案数,第二个是将 (S) 分配下去的方案数。

    对于第二部分,不难列生成函数得到答案为 (frac{1}{(1-x)^m}[x^{S-m}]=inom{S-1}{m-1})

    (G) 为第一部分答案的生成函数,那么答案即:

    [sum_{i=1}^{infty} G(x)[x^i]inom{i-1}{m-1} ]

    方便起见,令 (m=m-1),于是答案可以写为:

    [frac{1}{m!}sum_{i=1}^{infty} G(x)[x^i](i-1)^{underline{m}} ]

    即:

    [frac{1}{m!}sum_{i=0}^{infty} frac{G(x)}{x}[x^i]i^{underline{m}} ]

    这是注意到 (G(x)) 没有常数项,因为每个子函数都是 (x+x^2+...)

    考虑 (f(x)=a imes x^k)(m) 阶导,结果为 (k^{underline{m}} imes a imes x^{k-m}),所以不难发现答案即 (frac{1}{m!} imes G^{(m)}(1))

    接下来对于每个 (i) 列得一个生成函数,同时设 (F(x)=prod f_i(x)^{a_i}),那么考虑 (F^{(m)}(x)),其中 (f_0(x)=frac{1}{x})

    • 根据乘法法则 ((fcdot g)'=f'g+fg')
    • 考虑连续使用乘法法则,观察发现 ((fcdot gcdot h)'=f'gh+fg'h+fgh'),不难发现等价于乘法法则生效一次后不生效,或者从最外层开始剥?
    • 本质上乘法法则类似于 01 背包的模型,当某个 (i) 被求一次导之后其他元素不需要额外求导,配合加法法则,我们不难看出对于连乘求 (m) 阶导可以视为这样的一个流程:
    • 每轮选一个元素 (f),将其求一次导,对于所有方案求导得到的 (f) 序列相乘然后加起来。
    • 于是有 (F^{(m)}(x)=sum_{c_1,c_2...c_n} frac{m!}{c_1!c_2!...c_n!}[c_1+c_2+...+c_n=m] prod f_i^{(c_i)})
    • 也可以从生成函数的角度来理解,增添一个辅助变量 (z),那么答案即:

    [igg(prod_{i=1}^n (sum_{c=0}^{infty} frac{f_i^{(c)}(1)}{c!}z^c)^{a_i}igg)[z^m] ]

    这样可以考虑对于每个 (i) 列得一个长度为 (mathcal O(m)) 的生成函数,然后计算其 (a_i) 次幂的卷积和即可。

    我们唯一的问题是计算 (f_i^c(1)),不难注意到:

    对于 (c=0),有 (f_i^{(0)}(1)=b_i)

    对于 (c=k),有 (f_i^{(k)}(1)=sum_{jge k}^m j^{underline{k}})

    所以计算的和本质上是 (sum_{j=1}^{m}j^{underline{k}}),对于 (kge 1),注意到 (j^{underline{k}}=frac{(j+1)^{underline{k+1}}-j^{underline{k+1}}}{k+1})

    所以 (sum_{j=1}^{b_i} j^{underline{k}}=frac{1}{k+1}sum_{j=1}^m (j+1)^{underline{k+1}}-j^{underline{k+1}}=frac{(b_i+1)^{underline{k+1}}}{k+1})

    对于 (i=0)(f(x)=frac{1}{x}),于是 (f^{(k)}(x)=(-1)^k k!),所以 (frac{f^{(k)}(1)}{k!}=(-1)^K),这样暴力做 (n) 次卷积,每次 (mathcal O(mlog m)) 的处理,视快速幂是否写 (exp&ln) 复杂度为 (mathcal O(nmlog m)/O(nmlog mlog a))

    于是这个神仙题也就做完了。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define int long long
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int P = 998244353 ; 
    const int Gi = 332748118 ;
    const int G = 3 ; 
    const int N = 6e5 + 5 ;
    int n, m, limit, Inv, L, iv[N], inv[N], A[N], B[N], f[N], R[N], Ans[N] ; 
    int fpow(int x, int k) {
    	int ans = 1, base = x ; 
    	while(k) {
    		if(k & 1) ans = ans * base % P ; 
    		base = base * base % P, k >>= 1 ;  
    	} return ans ; 
    }
    void init(int x) {
    	limit = 1, L = 0 ; while( limit < x ) limit <<= 1, ++ L ; 
    	for(re int i = 0; i < limit; ++ i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1)) ; 
    	Inv = fpow( limit, P - 2 ) ; 
    }
    void NTT(int *a, int type) {
    	for(re int i = 0; i < limit; ++ i) if(R[i] > i) swap(a[i], a[R[i]]) ;
    	for(re int k = 1; k < limit; k <<= 1 ) {
    		int d = fpow((type == 1) ? G : Gi, (P - 1) / (k << 1) ) ;
    		for(re int i = 0; i < limit; i += (k << 1) )
    		for(re int j = i, g = 1; j < i + k; ++ j, g = g * d % P ) {
    			int nx = a[j], ny = a[j + k] * g % P ; 
    			a[j] = (nx + ny) % P, a[j + k] = (nx - ny + P) % P ; 
    		}
    	} if( !type ) rep( i, 0, limit ) a[i] = a[i] * Inv % P ; 
    }
    int _Hx[N], _Base[N], Base[N] ; 
    void Fpow(int k) {
    	_Hx[0] = 1 ; rep( i, 0, m ) Base[i] = f[i] ;
    	while(k) {
    		rep( i, 0, m ) _Base[i] = Base[i] ; NTT(_Base, 1) ; 
    		if(k & 1) {
    			NTT(_Hx, 1) ; rep( i, 0, limit ) _Hx[i] = _Hx[i] * _Base[i] % P ; 
    			NTT(_Hx, 0) ; rep( i, m + 1, limit ) _Hx[i] = 0 ; 
    		}
    		rep( i, 0, limit ) _Base[i] = _Base[i] * _Base[i] % P ; 
    		NTT(_Base, 0) ;
    		rep( i, 0, m ) Base[i] = _Base[i] ; 
    		rep( i, 0, limit ) _Base[i] = 0 ; k >>= 1 ; 
    	}
    	rep( i, 0, m ) f[i] = _Hx[i] ; 
    	rep( i, 0, limit ) Base[i] = _Base[i] = _Hx[i] = 0 ; 
    }
    signed main()
    {
    	m = gi() - 1, n = gi() ; 
    	rep( i, 1, n ) A[i] = gi(), B[i] = gi() ; inv[0] = iv[0] = 1 ; 
    	rep( i, 1, m + 10 ) 
    		iv[i] = fpow( i, P - 2 ), inv[i] = inv[i - 1] * iv[i] % P ; 
    	init(m + m + 5) ;
    	rep( i, 0, m ) Ans[i] = (i & 1) ? P - 1 : 1 ;
    	rep( i, 1, n ) {
    		f[0] = B[i] ; int t = (B[i] + 1) % P ; 
    		rep( k, 1, m ) 
    			t = t * (B[i] - k + 1) % P, 
    			f[k] = iv[k + 1] * t % P * inv[k] % P ; 
    		Fpow(A[i]), NTT(f, 1), NTT(Ans, 1) ;
    		rep( k, 0, limit ) Ans[k] = Ans[k] * f[k] % P ; 
    		NTT(Ans, 0) ; rep( k, m + 1, limit ) Ans[k] = 0 ;
    		rep( k, 0, limit ) f[k] = 0 ; 
    	}
    	printf("%lld
    ", Ans[m] % P ) ; 
    	return 0 ;
    }
    
  • 相关阅读:
    page load时执行JavaScript
    解决Postgres无法连接的问题
    Linux环境设置IP及关闭防火墙
    解决VisualStudio无法调试的问题
    【PostgresSQL】同时更新两个表
    更改系统键盘
    【SQLSERVER】How to check current pool size
    BZOJ 1070: [SCOI2007]修车
    BZOJ 1069: [SCOI2007]最大土地面积
    BZOJ 1068: [SCOI2007]压缩
  • 原文地址:https://www.cnblogs.com/Soulist/p/13608051.html
Copyright © 2011-2022 走看看