[CTSC2018]假面

题意

n n 个人,Q个操作 , , i个人有 mi m i 滴血 , , 有两种操作

1. p p 的概率使u 1 1 点血

2.给出 k k 个人,从这些人中等概率地选出 1 1 ,求每个人被选中的概率

最后求每个人剩余血量的期望


题解

考虑怎么求每个人剩余的血的期望

pu[i] p u [ i ] 表示 u u 剩余i点血的概率 Eu=mii=0ipu[i] ⇒ E u = ∑ i = 0 m i i p u [ i ]

这个 pu p u 是可以用背包维护的 ( ( 而且非常好理解,初值 pu[mu]=1) p u [ m u ] = 1 )

pu[0]=pu[0]+pu[1]p p u ′ [ 0 ] = p u [ 0 ] + p u [ 1 ] ∗ p

pu[i]=pu[i](1p)+pu[i+1]p,i[1,mu] p u ′ [ i ] = p u [ i ] ∗ ( 1 − p ) + p u [ i + 1 ] ∗ p , i ∈ [ 1 , m u ]

操作 1 1 就用上面的dp维护即可

考虑怎么处理操作 2 2

实际上操作2只需要知道每个人活下来的概率即可 , , pxu=1pu[0]

对于 k k 个人中的每个人用简单的数学知识可以得到选中u的概率是

pxui=0k1fu[i]i+1 p x u ∑ i = 0 k − 1 f u [ i ] i + 1

fu[i] f u [ i ] 表示除了 u u 之外只有i个人活着的概率

枚举另外一个人 v, v , 可以得到

fu[i]=pxvfu[i1]+(1pxv)fu[i] f u ′ [ i ] = p x v ∗ f u [ i − 1 ] + ( 1 − p x v ) ∗ f u [ i ]

于是乎对于每一个人做一遍这样的 dp d p 即可

复杂度 O(Qm+Cn3) O ( Q m + C n 3 ) 显然过不了

考虑如果知道还活着任意 i i 个人的概率g[i] pxu, p x u , 能不能知道 fu[i]? f u [ i ] ?

显然是可以的可以发现上面那个 dp d p 没有人的限制 , , 所以可以倒过来

fu[i]=fu[i]pxvfu[i1]1pxv

因为这个 v v 是随意的,所以有

fu[i]=g[i]pxufu[i1]1pxu f u [ i ] = g [ i ] − p x u ∗ f u [ i − 1 ] 1 − p x u

pxu=1 p x u = 1 fu[i]=g[i+1] f u [ i ] = g [ i + 1 ]

g g 可以通过一个n2 dp d p 求出

g[i]=pxug[i1]+(1pxu)g[i] g [ i ] = p x u ∗ g [ i − 1 ] + ( 1 − p x u ) ∗ g [ i ]

对于每一个人只要再 O(n) O ( n ) 逆转移一下即可

总时间复杂度 O(Qm+Cn2) O ( Q m + C n 2 )

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
    while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
    if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=205,P=998244353;
typedef int arr[N];
typedef long long ll;
int n,m,q;arr f,g,w,px,inv,p[N];
int Inv(int x){return x<N?inv[x]:(ll)(P-P/x)*Inv(P%x)%P;}
inline int pls(int a,int b){return a+=b,a<P?a:a-P;}
inline int sub(int a,int b){return a-=b,a<0?a+P:a;}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(n);
    fp(i,1,n)sd(w[i]),p[i][w[i]]=1;inv[1]=1;
    fp(i,2,N-1)inv[i]=(ll)(P-P/i)*inv[P%i]%P;
    sd(q);int op,x,a,b,tp=0;
    while(q--){
        sd(op);
        if(op){
            sd(m);fp(i,1,m)sd(x),px[i]=sub(1,p[x][0]),g[i]=0;g[0]=1;
            fp(i,1,m)fd(j,i,0)g[j]=((j?(ll)px[i]*g[j-1]%P:0ll)+(ll)sub(1,px[i])*g[j])%P;
            fp(i,1,m){
                if(!px[i]){we(0);continue;}
                if(px[i]==1)fp(j,0,m-1)f[j]=g[j+1];
                else{
                    x=Inv(sub(1,px[i]));f[0]=(ll)g[0]*x%P;
                    fp(j,1,m-1)f[j]=(ll)sub(g[j],(ll)f[j-1]*px[i]%P)*x%P;
                }
                fp(j,0,m-1)tp=pls(tp,(ll)f[j]*inv[j+1]%P);
                we((ll)px[i]*tp%P);tp=0;
            }sr[++C]='\n';
        }else{
            sd(x),sd(a),sd(b);
            a=(ll)a*Inv(b)%P,b=sub(1,a);
            p[x][0]=pls(p[x][0],(ll)p[x][1]*a%P);
            fp(i,1,w[x])p[x][i]=((ll)p[x][i+1]*a+(ll)p[x][i]*b)%P;
        }
    }
    fp(i,1,n){
        tp=0;
        fp(j,1,w[i])tp=pls(tp,(ll)j*p[i][j]%P);
        we(tp);
    }
return Ot(),0;
}

出题人说这是一道NOIP中档题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值