P4564-[CTSC2018]假面【期望dp】

ssl_wyc發表於2020-10-06

正題

題目大意:https://www.luogu.com.cn/problem/P4564


題目大意

n n n個人第 i i i個有 m i m_i mi點血,每次有操作

  1. p p p的概率對一個人造成 1 1 1點傷害(如果死了就不算, p p p每次都不同)
  2. 給出若干個人,對裡面存活的人隨機選擇一個,求每個人被選中的概率

最後要求輸出每個人的期望血量


解題思路

p i , j p_{i,j} pi,j表示第 i i i個人剩餘 j j j點血的概率。這個可以 O ( Q n ) O(Qn) O(Qn)的時間內維護。

考慮如何計算概率,因為存活人數的不同,產生的貢獻也不同,我們設 f i , j f_{i,j} fi,j表示第 i i i個人以外的人存活了 j j j個的概率,這個很容易可以在 O ( C n 2 ) O(Cn^2) O(Cn2)的時間內算,但是這樣顯然過不去。

所以我們要進行優化,我們可以在 O ( n 2 ) O(n^2) O(n2)的時間內算出 g i g_i gi表示所有人裡存活 i i i個人的概率,也就是有(以下為了方便定義 p i p_i pi表示 1 − p i , 0 1-p_{i,0} 1pi,0即第 i i i個人存活的概率):
g i = g i − 1 ∗ p u + g i ∗ ( 1 − p u ) g_i=g_{i-1}*p_u+g_{i}*(1-p_u) gi=gi1pu+gi(1pu)

顯然我們可以從 f u f_{u} fu推到 g g g
g i = f u , i ∗ ( 1 − p u ) + f u , i − 1 ∗ p u g_i=f_{u,i}*(1-p_u)+f_{u,i-1}*p_u gi=fu,i(1pu)+fu,i1pu可以回推回來也就是
⇒ f u , i = g i − f u , i − 1 ∗ p u 1 − p u \Rightarrow f_{u,i}=\frac{g_i-f_{u,i-1}*p_u}{1-p_u} fu,i=1pugifu,i1pu

這樣我們就可以在 O ( C n 2 ) O(Cn^2) O(Cn2)的時間內算出所有的 f u , i f_{u,i} fu,i來統計答案。因為求逆元也很慢,所以我們先線性推逆元預處理一下比較小的值。


c o d e code code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int XJQ=998244353;
int n,Q,p[210][210],c[210],g[210],f[210],in[210];
int power(int x,int b){
	int ans=1;x%=XJQ;
	while(b){
		if(b&1)ans=1ll*ans*x%XJQ;
		x=1ll*x*x%XJQ;b>>=1;
	}
	return ans;
}
int read() {
	int x=0,f=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
	return x*f;
}
void print(int x){
	if (x>9) print(x/10); putchar(x%10+48); return;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)p[i][read()]=1;
	Q=read();in[1]=1;
	for(int i=2;i<=n;i++)
		in[i]=(long long)XJQ-(long long)XJQ/i*in[XJQ%i]%XJQ;
	while(Q--){
		int op=read();
		if(op==0){
			int id=read(),u=read(),v=read();
			u=1ll*u*power(v,XJQ-2)%XJQ;
			p[id][0]=(p[id][0]+1ll*p[id][1]*u)%XJQ;
			for(int i=1;i<=100;i++)
				p[id][i]=(1ll*p[id][i+1]*u+1ll*p[id][i]*(1-u+XJQ))%XJQ;
		}
		else{
			int k=read(),x,ans=0;
			for(int i=1;i<=k;i++)c[i]=read();
			memset(g,0,sizeof(g));g[0]=1;
			for(int i=1;i<=k;i++){
				for(int j=i;j>=1;j--)
					g[j]=(1ll*g[j]*p[c[i]][0]+1ll*(1-p[c[i]][0]+XJQ)*g[j-1])%XJQ;
				g[0]=1ll*g[0]*p[c[i]][0]%XJQ;
			}
			for(int i=1;i<=k;i++){
				int ans=0,z=(1-p[c[i]][0]+XJQ)%XJQ,inv=power(p[c[i]][0],XJQ-2);
				if(p[c[i]][0]==1){
					printf("0 ");
					continue;
				}
				if(p[c[i]][0]==0)
					for(int j=0;j<k;j++)
						f[j]=g[j+1];
				else{
					f[0]=1ll*g[0]*inv%XJQ;
					for(int j=1;j<k;j++){
						f[j]=(g[j]-1ll*f[j-1]*z%XJQ+XJQ)%XJQ;
						f[j]=1ll*f[j]*inv%XJQ;
					}
				}
				for(int j=0;j<k;j++)
					(ans+=1ll*f[j]*in[j+1]%XJQ)%=XJQ;
				print(1ll*ans*z%XJQ);putchar(' ');
			}
			putchar('\n'); 
		}
	}
	for(int i=1;i<=n;i++){
		int ans=0;
		for(int j=1;j<=100;j++)
			ans=(ans+1ll*p[i][j]*j%XJQ)%XJQ;
		print(ans);putchar(' ');
	}
}

相關文章