Comet OJ Round11E ffort

题面描述

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

\(n\times m\le 10^5,a_i\le 10^5,b_i\in [1,998244352]\)

\(\rm Sol:\)

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

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

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

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

\[\sum_{i=1}^{\infty} G(x)[x^i]\binom{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\times x^k\)\(m\) 阶导,结果为 \(k^{\underline{m}}\times a\times x^{k-m}\),所以不难发现答案即 \(\frac{1}{m!}\times G^{(m)}(1)\)

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

  • 根据乘法法则 \((f\cdot g)'=f'g+fg'\)
  • 考虑连续使用乘法法则,观察发现 \((f\cdot g\cdot 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\),那么答案即:

\[\bigg(\prod_{i=1}^n (\sum_{c=0}^{\infty} \frac{f_i^{(c)}(1)}{c!}z^c)^{a_i}\bigg)[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_{j\ge k}^m j^{\underline{k}}\)

所以计算的和本质上是 \(\sum_{j=1}^{m}j^{\underline{k}}\),对于 \(k\ge 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(m\log m)\) 的处理,视快速幂是否写 \(\exp\&\ln\) 复杂度为 \(\mathcal O(nm\log m)/O(nm\log m\log 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\n", Ans[m] % P ) ; 
	return 0 ;
}
posted @ 2020-09-03 15:57  Soulist  阅读(119)  评论(0编辑  收藏  举报