zoukankan      html  css  js  c++  java
  • [CTSC2018]假面

    题目大意:
      有$n(nle200)$个人,每个人初始血量为$m_i(m_ile100)$对这些人进行$q(qle2 imes10^5)$次操作,操作包含以下两种:
        1. 选择编号为$id$的人,有$p$的概率扣掉一滴血;
        2. 编号为$id_1,id_2,ldots,id_k$的$k$个人,等概率的从这$k$个人中选取一个活着的人封印,问这$k$个人中每个人被封印的概率是多少。
      处理完所有操作后,求每个人最终血量的期望。

    思路:
      $f[i][j]$表示第$i$个人血量为$j$的概率,则对于每次的操作1,有转移方程$f'[i][j]=f[i][j] imes(1-p)+f[i][j+1] imes p$。特别地,对于$j=0$的情况,$f'[i][0]=f[i][0]+f[i][1] imes p$,即,血量为$0$时,不管怎么扣血,血量仍旧为$0$。
      考虑操作$2$,用$alive(x)$和$dead(x)$分别表示$x$在当前局面下,存活或死亡的概率。用$g[j]$表示当前集合中不包括当前封印的有$j$人存活的概率,每次把新人$x$加入到集合中时,转移方程为$g'[j]=alive(x) imes g[j-1]+dead(x) imes g[j]$。若封印的人为$i$,则答案为$alive(i) imessum_{j=0}^{k-1}frac{g[j]}{j+1}$。每次$O(k)$枚举封印的人,剩下$O(k^2)$DP,则对于每次操作2,时间复杂度为$O(k^3)$,有70分。
      发现上述状态转移可逆,即$g[j]=frac{g'[j]-alive(x) imes g[j-1]}{dead(x)}$,因此可以不考虑封印的人求出$g$数组,再枚举封印的人进行逆转移,单次操作时间复杂度$O(k^2)$。
      对于期望血量,直接利用$f$数组求得。
      此外本题有些卡常,需要开O2。

     1 #pragma GCC optimize("Ofast")
     2 #include<cstdio>
     3 #include<cctype>
     4 #include<algorithm>
     5 typedef long long int64;
     6 inline int getint() {
     7     register char ch;
     8     while(!isdigit(ch=getchar()));
     9     register int x=ch^'0';
    10     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    11     return x;
    12 }
    13 const int N=201,M=101,mod=998244353;
    14 int m[N],f[N][M],g[N],h[N],id[N],map[N];
    15 void exgcd(const int &a,const int &b,int &x,int &y) {
    16     if(!b) {
    17         x=1,y=0;
    18         return;
    19     }
    20     exgcd(b,a%b,y,x);
    21     y-=a/b*x;
    22 }
    23 inline int inv(const int &x) {
    24     if(x<N&&map[x]) return map[x];
    25     int ret,tmp;
    26     exgcd(x,mod,ret,tmp);
    27     const int ans=(ret%mod+mod)%mod;
    28     if(x<N) map[x]=ans;
    29     return ans;
    30 }
    31 inline int alive(const int &x) {
    32     return mod-f[x][0]+1;
    33 }
    34 inline int dead(const int &x) {
    35     return f[x][0];
    36 }
    37 int main() {
    38     const int n=getint();
    39     for(register int i=1;i<=n;i++) {
    40         f[i][m[i]=getint()]=1;
    41     }
    42     const int q=getint();
    43     for(register int i=0;i<q;i++) {
    44         const int opt=getint();
    45         if(opt==0) {
    46             const int id=getint(),u=getint(),v=getint(),p=(int64)u*inv(v)%mod;
    47             (f[id][0]+=(int64)f[id][1]*p%mod)%=mod;
    48             for(register int i=1;i<=m[id];i++) {
    49                 f[id][i]=(int64)f[id][i]*(mod-p+1)%mod;
    50                 if(i!=m[id]) (f[id][i]+=(int64)f[id][i+1]*p%mod)%=mod;
    51             }
    52         }
    53         if(opt==1) {
    54             const int k=getint();
    55             std::fill(&g[g[0]=1],&g[k]+1,0);
    56             for(register int i=1;i<=k;i++) {
    57                 id[i]=getint();
    58                 for(register int j=i;~j;j--) {
    59                     g[j]=(int64)dead(id[i])*g[j]%mod;
    60                     if(j>0) (g[j]+=(int64)alive(id[i])*g[j-1]%mod)%=mod;
    61                 }
    62             }
    63             for(register int i=1;i<=k;i++) {
    64                 int ans=0;
    65                 if(!dead(id[i])) {
    66                     for(register int j=0;j<k;j++) {
    67                         (ans+=(int64)g[j+1]*inv(j+1)%mod)%=mod;
    68                     }
    69                 } else {
    70                     std::fill(&h[h[0]=1],&h[k],0);
    71                     for(register int j=1;j<=k;j++) {
    72                         if(i!=j) h[0]=(int64)h[0]*dead(id[j])%mod;
    73                     }
    74                     for(register int j=0;j<k;j++) {
    75                         if(j>0) h[j]=(int64)((g[j]-(int64)alive(id[i])*h[j-1]%mod+mod)%mod)*inv(dead(id[i]))%mod;
    76                         (ans+=(int64)h[j]*inv(j+1)%mod)%=mod;
    77                     }
    78                 }
    79                 ans=(int64)ans*alive(id[i])%mod;
    80                 printf("%d%c",ans," 
    "[i==k]);
    81             }
    82         }
    83     }
    84     for(register int i=1;i<=n;i++) {
    85         int ans=0;
    86         for(register int j=1;j<=m[i];j++) {
    87             (ans+=(int64)j*f[i][j]%mod)%=mod;
    88         }
    89         printf("%d%c",ans," 
    "[i==n]);
    90     }
    91     return 0;
    92 }
  • 相关阅读:
    SpringBoot中添加事务
    隐藏样式
    Mybatis配置解析
    题目1064:反序数------玩转小聪明
    题目1063:整数和
    题目1062:分段函数23333333333333
    题目1060:完数VS盈数------这题做得我想骂人
    题目1059:abc----------就喜欢这样的题
    题目1050:完数-----------runtime error的问题
    题目1049:字符串去特定字符
  • 原文地址:https://www.cnblogs.com/skylee03/p/9026555.html
Copyright © 2011-2022 走看看