题解 P4564 【[CTSC2018]假面】

xukuan

2020-08-11 16:00:31

Solution

首先这题时间限制可能有鬼,xjoi上时6s,这份代码在xjoi103上要5-5.2s **本题解的推导不包括取模** 这题直接维护期望会非常难理解,由于概率非常好处理,所以维护概率 设$f_{i,j}$表示当前第i个人的血量为j的概率 转移方程只有2中情况:被打了掉血或不掉血。特别的,0血的人不会被打 $ f_{i,j}=\left\{ \begin{aligned} f_{i,j+1}*p+f_{i,j}*(1-p),j \geq 1\\ f_{i,j+1}*p+f_{i,j},j=0\\ \end{aligned} \right. $ 对于每一个怪而言: 期望直接用 $\sum$概率\*收益 来求 dp类似01背包的压维,倒着跑 $ dp_j=\left\{ \begin{aligned} g_i*dp_{j-1}+(1-g_i)*dp_j,j \geq 1\\ (1-g_i)*dp_j,j=0 \end{aligned} \right. $ 理论上可以出答案了,但跑的太慢,所以要优化。 活着就是血量不为0,概率可以直接得出 $g_i=1-f_{i,0}$ 然后继续推 没人死就直接抄:当$g_i=1$时$sum_j=dp_{j+1}$ 因为是在不死的人里面选, $ sum_j=\left\{ \begin{aligned} \frac{dp_j+sum_{j-1}*g_i}{1-g_i},j \geq 1\\ \frac{dp_j}{1-g_i},j=0\\ \end{aligned} \right. $ 代码: ```cpp #include<bits/stdc++.h> #include<iostream> #include<cstdio> #define ll long long using namespace std; const ll N=2010,mod=998244353; ll n,a[N],f[N][N],g[N],dp[N],sum[N]; inline ll read(){ ll x=0,tmp=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') tmp=-1; ch=getchar(); } while(isdigit(ch)){ x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return tmp*x; } inline void write(ll x){ if(x<0){ putchar('-'); x=-x; } ll y=10,len=1; while(y<=x){ y=(y<<3)+(y<<1); len++; } while(len--){ y/=10; putchar(x/y+48); x%=y; } } inline ll ksm(ll x,ll y){ ll ans=1; while(y){ if(y&1) ans=ans*x%mod; x=x*x%mod; y>>=1; } return ans; } inline ll inv(ll x){ return ksm(x,mod-2); } int main(){ n=read(); for(ll i=1; i<=n; i++){ a[i]=read(); f[i][a[i]]=1; } for(ll q=read(); q; q--){ ll op=read(); if(op==0){ ll x=read(),fenzi=read(),fenmu=read(); ll gailv=fenzi*inv(fenmu)%mod; f[x][0]=(f[x][0]+f[x][1]*gailv%mod)%mod; for(ll i=1; i<=a[x]; i++) f[x][i]=(f[x][i]*(mod+1-gailv)%mod+f[x][i+1]*gailv%mod)%mod; } else if(op==1){ ll m=read(); for(ll i=1; i<=m; i++){ ll x=read(); g[i]=(mod+1-f[x][0])%mod; } memset(dp,0,sizeof(dp)); dp[0]=1; for(ll i=1; i<=m; i++){ for(ll j=i; j>=0; j--){ if(j) dp[j]=(g[i]*dp[j-1]%mod+(mod+1-g[i])%mod*dp[j]%mod)%mod; else dp[j]=(mod+1-g[i])%mod*dp[j]%mod; } } for(ll i=1; i<=m; i++){ if(!g[i]){ putchar('0'); putchar(' '); continue; } if(g[i]==1){ for(ll j=0; j<m; j++) sum[j]=dp[j+1]; } else{ ll x=inv((mod+1-g[i])%mod); sum[0]=dp[0]*x%mod; for(ll j=1; j<m; j++) sum[j]=(dp[j]+mod-sum[j-1]*g[i]%mod)%mod*x%mod; } ll ans=0; for(ll j=0; j<m; j++) ans=(ans+sum[j]*inv(j+1)%mod)%mod; write(g[i]*ans%mod); putchar(' '); } putchar('\n'); } else cout<<"FUCK "<<op<<endl; } for(ll i=1; i<=n; i++){ ll ans=0; for(ll j=1; j<=a[i]; j++) ans=(ans+j*f[i][j])%mod; write(ans); putchar(' '); } putchar('\n'); return 0; } ```