[ACNOI2022]张士超的钥匙

280 篇文章 1 订阅

题目

题目背景
“想成为标杆的话,绝对没有捷径可走;成为标杆之后,更是无路可退!” D D ( X Y X ) \sf DD(XYX) DD(XYX) 从来没有忘记自己的使命。“所谓标杆,就是忍受一切赞誉走在所有人前面的人!”

我最大的错误就是试图与 D D ( X Y X ) \sf DD(XYX) DD(XYX) 相比较。而有的错误需要用一生来偿还。

题目描述
在第 i    ( 1 ⩽ i ⩽ m ) i\;(1\leqslant i\leqslant m) i(1im) 天, O n e I n D a r k \sf OneInDark OneInDark p i p_i pi 的概率参加考试,然后被 D D ( X Y X ) \sf DD(XYX) DD(XYX) 超越 b i b_i bi 道题;也有 ( 1 − p i ) (1-p_i) (1pi) 的概率不参加考试,什么事儿也不发生。若 m m m 天过后, O n e I n D a r k \sf OneInDark OneInDark D D ( X Y X ) \sf DD(XYX) DD(XYX) 总共超越了至少 n n n 道题,则称为 “太阳打东边出来”。

现在请对所有 { b }    ( 0 ⩽ b i ⩽ a i ) \{b\}\;(0\leqslant b_i\leqslant a_i) {b}(0biai) 满足 ∑ b i = N \sum b_i=N bi=N 求出 “太阳打东边出来” 的概率之和。

数据范围与提示
m ⩽ 100 m\leqslant 100 m100 1 ⩽ n ⩽ N ⩽ min ⁡ ( 1 0 9 , ∑ a i ) 1\leqslant n\leqslant N\leqslant \min(10^9,\sum a_i) 1nNmin(109,ai) 。输入中还会给出非负整数 d d d 使得 d ∣ ( a i + 1 ) d\mid(a_i+1) d(ai+1) N ⩽ 100 d N\leqslant 100d N100d 。其余的范围如 a i ⩽ 1 0 9 ,    d ⩽ 1 0 7 a_i\leqslant 10^9,\;d\leqslant 10^7 ai109,d107 无甚用处。输出对 998244353 998244353 998244353 取模。

思路

有一个奇怪的条件 d ∣ ( a i + 1 ) d\mid(a_i+1) d(ai+1) 。它会在哪里用上呢?

解决 n = 1 n=1 n=1 n = N n=N n=N 的子任务时,就发现总方案数的计算可以是 [ x N ] ∏ 1 − x a i + 1 1 − x [x^N]\prod\frac{1-x^{a_i+1}}{1-x} [xN]1x1xai+1,那么分子实际上是一个关于 x d x^d xd 的多项式,长度不超过 N d ⩽ 100 {N\over d}\leqslant 100 dN100 了。最后乘一个 ( 1 − x ) − m (1-x)^{-m} (1x)m,简单组合数即可解决。

再接再厉,继续考虑生成函数。可以发见,目前的 ∑ b \sum b b 和 “已经拿到手的钥匙” 数量是有用而独立的,于是需要写成二元生成函数
F i ( x , y ) = ∑ j = 0 a i x j ( p i y j + 1 − p i ) = p i ⋅ 1 − ( x y ) a i + 1 1 − x y + ( 1 − p i ) ⋅ 1 − x a i + 1 1 − x \begin{aligned} F_i(x,y) &=\sum_{j=0}^{a_i}x^j(p_iy^j+1-p_i)\\ &=p_i\cdot {1-(xy)^{a_i+1}\over 1-xy}+(1-p_i)\cdot\frac{1-x^{a_i+1}}{1-x} \end{aligned} Fi(x,y)=j=0aixj(piyj+1pi)=pi1xy1(xy)ai+1+(1pi)1x1xai+1

然后答案就是 ∑ i ⩾ n [ x N y i ] ∏ j F j ( x , y ) \sum_{i\geqslant n}[x^Ny^i]\prod_j F_j(x,y) in[xNyi]jFj(x,y) 。这东西怎么求解呢?

显然我们想要保留分子的指数 ( a i + 1 ) (a_i{\rm+}1) (ai+1),所以分母就应单独记录。即,记录 O ( m ) \mathcal O(m) O(m) 个多项式(的分子),分母为 ( 1 − x y ) i ⋅ ( 1 − x ) m − i (1-xy)^i\cdot(1-x)^{m-i} (1xy)i(1x)mi

那么我们顺序做乘法,得到的结果等价于求出了 f ( a , b , c , d ) f(a,b,c,d) f(a,b,c,d) 表示分母为 ( 1 − x y ) a ( 1 − x ) b (1-xy)^a(1-x)^b (1xy)a(1x)b x c y d x^cy^d xcyd 的系数。由于是 O ( 1 ) \mathcal O(1) O(1) 转移,复杂度当然是状态数 O [ m 2 ( N d ) 2 ] \mathcal O[m^2({N\over d})^2] O[m2(dN)2]

最后需要除以 ( 1 − x y ) a ( 1 − x ) b (1-xy)^a(1-x)^b (1xy)a(1x)b,等价于乘上 ∑ i ⩾ 0 ( i + a − 1 i ) ( x y ) i \sum_{i\geqslant 0}{i+a-1\choose i}(xy)^i i0(ii+a1)(xy)i ∑ i ⩾ 0 ( i + b − 1 i ) x i \sum_{i\geqslant 0}{i+b-1\choose i}x^i i0(ii+b1)xi,即乘上
∑ i ⩾ 0 x i ∑ j = 0 i ( j + a − 1 j ) ( i − j + b − 1 i − j ) y j \sum_{i\geqslant 0}x^i\sum_{j=0}^{i}{j+a-1\choose j}{i-j+b-1\choose i-j}y^j i0xij=0i(jj+a1)(ijij+b1)yj

不幸的是,我们要计算 ∑ i ⩾ n [ y i ] F ( y ) \sum_{i\geqslant n}[y^i]F(y) in[yi]F(y) 。也就是说, O ( m 3 ) \mathcal O(m^3) O(m3) 枚举 a , b a,b a,b x , y x,y x,y 的指数后,我们面临的问题实际上是
∑ j = l i ( j + a − 1 j ) ( i − j + b − 1 i − j ) \sum_{j=l}^{i}{j+a-1\choose j}{i-j+b-1\choose i-j} j=li(jj+a1)(ijij+b1)

因为 y y y 的指数不小于某个阈值 l l l 即可。由于 i i i 的范围是 O ( n ) \mathcal O(n) O(n) 的,绝对不能这样计算。怎么办?

忠告:组合数应当递推计算。譬如这道题。尽管这好像并不很有逻辑。

上式也可写为 ∑ j = 0 i − l ( j + l + a − 1 j + l ) ( i − l − j + b − 1 i − l − j ) \sum_{j=0}^{i-l}{j+l+a-1\choose j+l}{i-l-j+b-1\choose i-l-j} j=0il(j+lj+l+a1)(iljilj+b1),为了递推的方便,不妨简记
S ( n 1 , m 1 ) = ∑ j = 0 n 2 ( j + n 1 + m 1 j + n 1 ) ( n 2 − j + m 2 n 2 − j ) S(n_1,m_1)=\sum_{j=0}^{n_2}{j+n_1+m_1\choose j+n_1}{n_2-j+m_2\choose n_2-j} S(n1,m1)=j=0n2(j+n1j+n1+m1)(n2jn2j+m2)

也就是用 n 1 = l ,    m 1 = a − 1 ,    n 2 = i − l ,    m 2 = b − 1 n_1=l,\;m_1=a-1,\;n_2=i-l,\;m_2=b-1 n1=l,m1=a1,n2=il,m2=b1 替换了一下。这里没有将 m 2 , n 2 m_2,n_2 m2,n2 设为自变量,因为后面的递推中二者是不变的。

最简单的方法:利用帕斯卡法则 减小上指标。比如将 ( j + n 1 + m 1 j + n 1 ) {j+n_1+m_1\choose j+n_1} (j+n1j+n1+m1) ( j + n 1 + m 1 − 1 j + n 1 ) + ( j + n 1 + m 1 − 1 j + n 1 − 1 ) {j+n_1+m_1-1\choose j+n_1}+{j+n_1+m_1-1\choose j+n_1-1} (j+n1j+n1+m11)+(j+n11j+n1+m11) 换掉,前者就得到 S ( n 1 , m 1 − 1 ) S(n_1,m_1{\rm-}1) S(n1,m11),而后者得到 S ( n 1 − 1 , m 1 ) S(n_1{\rm-}1,m_1) S(n11,m1) 。这相当于在坐标系内,沿着四连通的边移动;只需要任选一个边界。由于 ( j + n 1 ) (j{\rm+}n_1) (j+n1) 是下指标,必然有 n 1 ⩾ 0 n_1\geqslant 0 n10,取边界 n 1 = 0 n_1=0 n1=0 。但 m 1 m_1 m1 可以取任意值。观察发现, m 1 = − 1 m_1=-1 m1=1 可以使得计算量最小。

  • n 1 = 0 n_1=0 n1=0,则
    S ( 0 , m 1 ) = ∑ j = 0 n 2 ( j + m 1 j ) ( n 2 − j + m 2 n 2 − j ) = ∑ j = 0 n 2 ( j + m 1 m 1 ) ( n 2 − j + m 2 m 2 ) = ( m 1 + m 2 + n 2 + 1 m 1 + m 2 + 1 ) \begin{aligned} S(0,m_1) &=\sum_{j=0}^{n_2}{j+m_1\choose j}{n_2-j+m_2\choose n_2-j}\\ &=\sum_{j=0}^{n_2}{j+m_1\choose m_1}{n_2-j+m_2\choose m_2}\\ &={m_1+m_2+n_2+1\choose m_1+m_2+1} \end{aligned} S(0,m1)=j=0n2(jj+m1)(n2jn2j+m2)=j=0n2(m1j+m1)(m2n2j+m2)=(m1+m2+1m1+m2+n2+1)

最后一步可以由 constructive \text{constructive} constructive 的组合意义得到:在 ( j + m 1 ) (j{\rm+}m_1) (j+m1) 元素后,插入一个被选择元素。或者,从第一行直接交换上下标得到简单的二项式系数卷积恒等式。

  • m 1 = − 1 m_1=-1 m1=1,则
    S ( n 1 , − 1 ) = ∑ j = 0 n 2 ( j + n 1 − 1 j + n 1 ) ( n 2 − j + m 2 n 2 − j ) = [ n 1 = 0 ] ( n 2 + m 2 n 2 ) S(n_1,-1) =\sum_{j=0}^{n_2}{j+n_1-1\choose j+n_1}{n_2-j+m_2\choose n_2-j}\\ =[n_1=0]{n_2+m_2\choose n_2} S(n1,1)=j=0n2(j+n1j+n11)(n2jn2j+m2)=[n1=0](n2n2+m2)

因为左式只在 j = n 1 = 0 j=n_1=0 j=n1=0 时,得出非零值 1 1 1

枚举边界值,考虑其对 S ( n 1 , m 1 ) S(n_1,m_1) S(n1,m1) 的贡献,则
S ( n 1 , m 1 ) = ∑ i = 1 n 1 S ( i , − 1 ) ( n 1 + m 1 − i n 1 − i ) + ∑ j = 0 m 1 S ( 0 , j ) ( n 1 + m 1 − j − 1 m 1 − j ) = ∑ j = 0 m 1 ( j + m 2 + n 2 + 1 j + m 2 + 1 ) ( n 1 + m 1 − j − 1 m 1 − j ) \begin{aligned} S(n_1,m_1) &=\sum_{i=1}^{n_1}S(i,-1){n_1+m_1-i\choose n_1-i} +\sum_{j=0}^{m_1}S(0,j){n_1+m_1-j-1\choose m_1-j}\\ &=\sum_{j=0}^{m_1}{j+m_2+n_2+1\choose j+m_2+1}{n_1+m_1-j-1\choose m_1-j} \end{aligned} S(n1,m1)=i=1n1S(i,1)(n1in1+m1i)+j=0m1S(0,j)(m1jn1+m1j1)=j=0m1(j+m2+1j+m2+n2+1)(m1jn1+m1j1)

注意 “坐标系内行走” 的系数,是 ( 1 , j ) → ( n 1 , m 1 ) (1,j)\to(n_1,m_1) (1,j)(n1,m1),因为不能再经过 ( 0 , j + 1 ) (0,j{\rm+}1) (0,j+1) 了。

这是一个 O ( m ) \mathcal O(m) O(m) 的枚举,所以总时间复杂度 O ( m 4 ) \mathcal O(m^4) O(m4) case closed \text{case closed} case closed

再闲聊两句:原式 ∑ j = l i ( j + a − 1 j ) ( i − j + b − 1 i − j ) \sum_{j=l}^{i}{j+a-1\choose j}{i-j+b-1\choose i-j} j=li(jj+a1)(ijij+b1) 有组合意义,即前 a a a 个盒子放 j j j 个球、后 b b b 个盒子放 ( i − j ) (i{\rm-}j) (ij) 个球、可以空盒的方案数。唯一的限制是 j ⩾ l j\geqslant l jl,即前 a a a 个盒子里放了至少 l l l 个球。

高明的想法:转而枚举第 l l l 个球的位置。设为第 j    ( j ⩾ a ) j\;(j\geqslant a) j(ja) 个盒子,则前 ( l − 1 ) (l{\rm-}1) (l1) 个球也在这 j j j 个盒子中;后 ( i − l ) (i{\rm-}l) (il) 个球则在后 [ j , a + b ] [j,a{\rm+}b] [j,a+b] 号盒子中,于是
∑ j = a a + b ( l − 1 + j − 1 j − 1 ) ( a + b − j + 1 + i − l − 1 a + b − j + 1 − 1 ) \sum_{j=a}^{a+b}{l-1+j-1\choose j-1}{a+b-j+1+i-l-1\choose a+b-j+1-1} j=aa+b(j1l1+j1)(a+bj+11a+bj+1+il1)

你会发现,它和 S ( l , a − 1 ) S(l,a{\rm-}1) S(l,a1) 完全相同,只不过 S ( l , a − 1 ) S(l,a{\rm-}1) S(l,a1) 中枚举的 j j j 是这里的 ( a − j ) (a-j) (aj) 罢了。所以上面的代数推演都没必要,组合意义有时比数学推导更好用。倒不如说,只是因为它恰好有这样一个组合意义,数学推导才可能得到一个简单的式子呢!

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0; int c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 105, MOD = 998244353;
int inv[MAXN];
inline int getC(int n, int m){
	llong c = 1;
	for(int i=0; i!=m; ++i)
		c = c*(n-i)%MOD*inv[i+1]%MOD;
	return int(c);
}
inline int calc(int n1, int m1, int n2, int m2){
	if(m1 == -1) return n1 ? 0 : int(getC(n2+m2,m2)); // m2 != -1
	static int t1[MAXN], t2[MAXN]; int res = 0;
	t1[0] = 1, t2[0] = getC(m2+n2+1,m2+1);
	rep(j,1,m1) t1[j] = int(llong(n1+j-1)*t1[j-1]%MOD*inv[j]%MOD);
	rep(j,1,m1) t2[j] = int(llong(m2+n2+1+j)*t2[j-1]%MOD*inv[j+m2+1]%MOD);
	rep(j,0,m1) res = int((res+llong(t1[j])*t2[m1-j])%MOD);
	return res;
}

int dp[2][MAXN][MAXN][MAXN];
int main(){
	int m = readint(), d = readint();
	int N = readint(), n = readint();
	rep(i,(inv[1]=1)+1,m) inv[i] = int(
		llong(MOD-MOD/i)*inv[MOD%i]%MOD);

	const int Nd = N/d;
	****dp = 1; // x^0 y^0
	for(int i=1,fr=0,a; i<=m; ++i,fr^=1){
		a = (readint()+1)/d; llong p = readint();
		rep(j,0,i) rep(x,0,Nd) rep(y,0,x){
			int &v = dp[i&1][j][x][y] = int( (
				(x < a ? 0 : (p-1)*dp[fr][j][x-a][y])
				+ (MOD+1-p)*dp[fr][j][x][y] ) %MOD );
			if(!j) break; // cannot choose it here
			v = int((v+p*dp[fr][j-1][x][y])%MOD);
			if(x >= a && y >= a) v = int((v+
				(MOD-p)*dp[fr][j-1][x-a][y-a])%MOD);
		}
	}

	int ans = 0;
	rep(a,0,m) rep(x,0,Nd) rep(y,0,x) if(dp[m&1][a][x][y]){
		const int l = std::max(0,n-y*d);
		if(l > N-x*d) continue; // invalid
		ans = int((ans+llong(dp[m&1][a][x][y])
			*calc(l,a-1,N-x*d-l,m-a-1))%MOD);
	}
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值