题面描述
给定 (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) 为第一部分答案的生成函数,那么答案即:
方便起见,令 (m=m-1),于是答案可以写为:
即:
这是注意到 (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),那么答案即:
这样可以考虑对于每个 (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 ;
}