洛谷十月月賽T2[深海少女與胖頭魚]小結

Outer_Space_發表於2020-10-22

題面

洛谷十月月賽II T2
深海少女與胖頭魚
總共有 n n n 條帶 「聖盾」的「胖頭魚」和 m m m 條不帶聖盾的胖頭魚,每次等概率對一條存活的胖頭魚造成「劇毒」傷害。

現在想知道,期望造成多少次傷害可以殺死全部胖頭魚?

答案對 998244353 998244353 998244353 取模。

「聖盾」:當擁有聖盾的胖頭魚受到傷害時,免疫這條魚所受到的本次傷害。免疫傷害後,聖盾被破壞。
「胖頭魚」:在一條胖頭魚的聖盾被破壞後,給予其他所有沒有死亡且沒有聖盾的胖頭魚聖盾。
「劇毒」:立即殺死沒有聖盾的胖頭魚。

輸入:2 1 輸出:8
輸入:10 10 輸出:499122389
輸入:10 0 輸出:65
輸入:2 0 輸出:5

前置知識

數學期望

數學期望

離散型
如果隨機變數只取得有限個值或無窮能按一定次序一一列出,其值域為一個或若干個有限或無限區間,這樣的隨機變數稱為離散型隨機變數。
離散型隨機變數的一切可能的取值 x i x_i xi與對應的概率 p ( x i ) p(x_i) p(xi)乘積之和稱為該離散型隨機變數的數學期望(若該求和絕對收斂),記為 E ( x ) E(x) E(x)。它是簡單算術平均的一種推廣,類似加權平均。

計算公式: E ( X ) = ∑ k = 1 ∞ x k p k E(X)=\sum_{k=1}^\infin x_kp_k E(X)=k=1xkpk


快速冪

求a的n次方,即求 a n = a ∗ a ∗ a ∗ a ∗ . . . ∗ a a^n=a*a*a*a*...*a an=aaaa...a n n n a a a),但是當 a , n a,n a,n過大時,暴力的計算便不太適用了。
這時候我們就需要用到快速冪(<-標準解析)

快速冪,二進位制取冪(Binary Exponentiation,也稱平方法),是一個在 O ( l o g N ) O(logN) O(logN) 的時間內計算 a n a^n an 的小技巧,而暴力的計算需要 O ( N ) O(N) O(N) 的時間。而這個技巧也常常用在非計算的場景,因為它可以應用在任何具有結合律的運算中。

二進位制取冪的想法是,將取冪的任務按照指數的 二進位制表示 來分割成更小的任務

舉例:求 3 13 3^{13} 313
3 13 = 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 3^{13}=3*3*3*3*3*3*3*3*3*3*3*3*3 313=3333333333333

3 13 = 3 2 ∗ 3 2 ∗ 3 2 ∗ 3 2 ∗ 3 2 ∗ 3 2 ∗ 3 3^{13}=3^2*3^2*3^2*3^2*3^2*3^2*3 313=3232323232323

3 13 = 3 4 ∗ 3 4 ∗ 3 4 ∗ 3 3^{13}=3^4*3^4*3^4*3 313=3434343

3 13 = 3 8 ∗ 3 4 ∗ 3 3^{13}=3^8*3^4*3 313=38343

3 13 = 3 ( 1101 ) 2 = 3 8 ∗ 3 4 ∗ 3 3^{13}=3^{(1101)_2}=3^8*3^4*3 313=3(1101)2=38343

程式碼如下

int power(int a,int b,int mod)//快速冪求a^b
{
	int sum=1;//初始化答案
	while(b)
	{
		if(b & 1)
			sum=sum*a%mod;//b為奇數,就乘一個當前的a
		b>>=1;//向下取整
		a=a*a%mod;
	}
	return sum%mod;
}

逆元

本題中需要在除法中取模,但這在普通運算中不可行,那麼就要用到逆元的知識
逆元素(百度百科)
費馬小定理(OI-Wiki)

條件: p p p為素數, g c d ( p , a ) = = 1 gcd(p,a)==1 gcd(p,a)==1
x x x為在( m o d mod mod p p p)條件下 a a a的逆元

a x ≡ 1 ( m o d p ) ① ax\equiv 1 (mod p)① ax1(modp)

a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1(modp) ap11(modp)

⇔ a ∗ a p − 2 ≡ 1 ( m o d p ) ② \Leftrightarrow a*a^{p-2}\equiv 1(modp)② aap21(modp)

由①②等價可得

x ≡ a p − 2 ( m o d p ) x\equiv a^{p-2}(modp) xap2(modp)


題解

分析

期望DP

由子任務 m = 0 m=0 m=0得到啟發,先對 m = 0 m=0 m=0情況分析

I.先處理只有帶聖盾胖頭魚的情況

f u f_u fu表示 u u u條聖盾胖頭魚期望需要造成傷害的次數, f v f_v fv表示如圖所示中間狀態。首先我們需要消耗1次攻擊打破一個聖盾。
在這裡插入圖片描述

f u = f v + 1 f_u=f_v+1 fu=fv+1

那麼下一次攻擊會有兩種情況:

  1. 如果這次攻擊打到了沒有聖盾的那條魚,那麼這條魚死亡,狀態來到了 u − 1 u-1 u1條帶聖盾胖頭魚的狀態。事件概率為 1 u \dfrac{1}{u} u1
    在這裡插入圖片描述

f v 1 = 1 u ( f u − 1 + 1 ) f_{v1}=\dfrac{1}{u}(f_{u-1}+1) fv1=u1(fu1+1)

  1. 如果這次攻擊打到了有聖盾的任意一條魚,那麼本質上是換了一條魚沒聖盾。事件概率為 u − 1 u \dfrac{u-1}{u} uu1(這裡因為 f v f_v fv進入的狀態是已經破了一個聖盾的,所以要 − 1 -1 1)。

在這裡插入圖片描述

f v 2 = u − 1 u ( f u + 1 − 1 ) = u − 1 u f u f_{v2}=\dfrac{u-1}{u}(f_u+1-1)=\dfrac{u-1}{u}f_u fv2=uu1(fu+11)=uu1fu

根據上面的分析,可以得出轉移方程:

f i = 1 i ( f i − 1 + 1 ) + i − 1 i f i + 1 f_i=\dfrac{1}{i}(f_{i-1}+1)+\dfrac{i-1}{i}f_i+1 fi=i1(fi1+1)+ii1fi+1

進一步化簡可得:

f i = f i − 1 + i + 1 f_i=f_{i-1}+i+1 fi=fi1+i+1

f 1 = 2 f_1=2 f1=2

根據求和公式即可 O ( 1 ) O(1) O(1)求出 f i f_i fi

f i = ∑ i = 1 n ( i + 1 ) = i ( i + 3 ) 2 f_i=\sum^{n}_{i=1}(i+1)=\dfrac{i(i+3)}{2} fi=i=1n(i+1)=2i(i+3)

得到了 f n f_n fn,接下來分析 m ≠ 0 m\ne0 m=0的情況

II.再處理加入不帶聖盾的胖頭魚

g i g_i gi n n n條帶盾魚, i i i條不帶聖盾的魚期望需要造成傷害的次數。

  1. 如果這次攻擊打到了沒有聖盾的胖頭魚,那麼這條魚死亡,進入 g i − 1 g_{i-1} gi1的狀態。事件概率為 i n + i \dfrac{i}{n+i} n+ii

  2. 如果這次攻擊打到了有聖盾的胖頭魚,那麼其他胖頭魚都會獲得聖盾,進入 f n + i − 1 f_{n+i}-1 fn+i1的狀態(已經破了一個聖盾)。事件概率為 n n + i \dfrac{n}{n+i} n+in

理解了上面的 f i f_i fi,這段就很好理解了,不放圖了

歸納上式,得轉移方程:

g i = i n + i ( g i − 1 + 1 ) + n n + i f n + i g_i=\dfrac{i}{n+i}(g_{i-1}+1)+\dfrac{n}{n+i}f_{n+i} gi=n+ii(gi1+1)+n+infn+i

答案即為 g m g_m gm。可以先求出 f i f_i fi,然後遞推計算 g m g_m gm

AC Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define ll long long
#define MAXN 1000010
#define mod 998244353
ll n,m;
ll g[MAXN];
ll power(ll a,ll b)//快速冪求逆元
{
	ll sum=1;
	a%=mod;
	while(b)
	{
		if(b&1)
			sum=sum*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return sum%mod;
}
signed main(void)
{
	scanf("%lld%lld",&n,&m);
	ll p_2=power(2,mod-2)%mod;//2在(%mod)條件下的逆元 
	g[0]=( (p_2%mod * (n%mod))%mod * (n%mod+3)%mod )%mod;//好事多模
	//g[0]即為全部都是有聖盾洗頭魚,初始化為g[0]=f[n]; 
	for(ll i=1;i<=m;i++)
	{
		ll p_ni=power( (n%mod+i)%mod , mod-2 ) %mod;//(n+i)在(%mod)條件下的逆元 
		
		ll k_n=( (p_2 * (n%mod))%mod * (n%mod+i+3)%mod )%mod;
		
		g[i]=( ( i * (g[i-1]%mod+1 )%mod * p_ni)%mod + k_n )%mod;
	}
	printf("%lld\n",g[m]%mod);//結束
	return 0;
}