【loj2552】【CTSC2018】假面

题目

\(n\)个敌方单位,初始生命值分别为\(m_1,\cdots,m_n\) ;

假面可以释放\(Q\)个技能:

$op = 0  ,  id , u , v $ 表示对\(id\)号敌人有\(\frac{u}{v}\)的概率造成\(1\)点伤害;

$op = 1  ,  k  ,  a_1,\cdots a_k $  表示在这些位置中生命值为正的位置里随机选择一个位置释放结界;

你需要对每个\(op=1\),输出\(a_1,\cdots, \ a_k\)中结界的期望(\(op=1\)操作最多\(C\)次);

$n \le 200  ,  Q \le 200000  ,  C \le 1000 ,m_i \le 100 $ ;

题解

  • Part 1

  • \(f_{i,j}\)表示\(i\)受到的伤害为\(j\)的概率,设\(x=\frac{u}{v}\),则\(f_{i,j} = x \times f_{i,j-1} + (1-x) \times f_{i,j}\)

  • \(p_i\)表示\(i\)号敌人存活的概率,$p_i  =   \sum_{j=0}^{m_i-1} f_{i,j} $

  • 剩余生命的期望\(q_i = \sum_{j=0}^{m_i-1}f_{i,j} \times (m_i-j)\)

  • Part 2

  • 把所有\(a_i\)拎出来讨论,设存活概率为\(p_i\) , \(g_{i,j}\)表示不算第\(i\)个敌人,存活的个数为\(j\)的概率,\(G_j\)表示所有人都算上存活个数为\(j\)的概率,一个人对于\(G\)的贡献是:

    $G_{j} = p_i \times G_{j-1}  +   (1-p_i) \times G_{j} $

  • 这个式子是可逆的,即:
    \[ \begin{cases} g_{i,j} &= \frac{G_j - p_i \times g_{i,j-1}}{1-p_i} & p_i != 1 \\ g_{i,j} &= G_{j+1} & p_i==1 \end{cases} \]

  • 时间复杂度:\(O(QM\ + \ nMC)\)

    #include<bits/stdc++.h>
    #define vec vector<int>
    #define pb push_back
    #define mod 998244353
    #define ll long long
    using namespace std;
    const int N=810;
    int n,m,a[N],b[N],f[N][N],tot,ny[N],p[N],g[N],t[N];
    char gc(){
      static char*p1,*p2,s[1000000];
      if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
      return(p1==p2)?EOF:*p1++;
    }
    int rd(){
      int x=0;char c=gc();
      while(c<'0'||c>'9')c=gc();
      while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
      return x;
    }
    int pw(int x,int y){
      int re=1;
      if(y<0)y+=mod-1;
      while(y){
          if(y&1)re=(ll)re*x%mod;
          y>>=1;x=(ll)x*x%mod;
      }
      return re;
    }
    void inc(int&x,int y){x+=y;if(x>=mod)x-=mod;}
    void solve(){
      for(int i=1;i<=tot;++i)g[i]=0;g[0]=1;
      for(int i=1;i<=tot;++i){
          int x=b[i],y=(mod+1-x)%mod;
          for(int j=tot;~j;--j){
              g[j]=(ll)g[j]*y%mod;
              if(j)inc(g[j],(ll)g[j-1]*x%mod);
          }
      }
      for(int j=0;j<=tot;++j)t[j]=g[j];
      for(int i=1;i<=tot;++i){
          int re=0,x=b[i],y=pw((mod+1-x)%mod,mod-2);
          if(x==1){for(int j=0;j<tot;++j)g[j]=g[j+1];g[tot]=0;}
          else{
              g[0]=(ll)g[0]*y%mod;
              for(int j=1;j<=tot;++j)g[j]=(g[j]-(ll)g[j-1]*x%mod+mod)%mod*y%mod;
          }
          for(int j=0;j<=tot;++j){
              inc(re,(ll)ny[j+1]*b[i]%mod*g[j]%mod);
              g[j]=t[j];
          }
          printf("%d ",re);
      }
      puts("");
    }
    int main(){
    //    freopen("faceless.in","r",stdin);
    //    freopen("faceless.out","w",stdout);
      n=rd();
      for(int i=1;i<=n;++i)a[i]=rd(),f[i][0]=p[i]=1;
      ny[1]=1;for(int i=2;i<=n<<2;++i)ny[i]=1ll*(mod-mod/i)*ny[mod%i]%mod;
      m=rd();
      for(int i=1;i<=m;++i){
          int op,id,x,y;
          op=rd();
          if(!op){
              id=rd();x=rd();y=rd();
              x=(ll)x*pw(y,mod-2)%mod;
              y=(mod+1-x)%mod;p[id]=0;
              for(int j=a[id]-1;~j;--j){
                  f[id][j]=(ll)f[id][j]*y%mod;
                  if(j)inc(f[id][j],(ll)f[id][j-1]*x%mod);
                  inc(p[id],f[id][j]);
              }
          }else {
              tot=rd();
              for(int j=1;j<=tot;++j)b[j]=p[rd()];
              solve();
          }
      }
      for(int i=1;i<=n;++i){
          int x=0;
          for(int j=0;j<a[i];++j)inc(x,(ll)(a[i]-j)*f[i][j]%mod);
          printf("%d ",x);
      }
      return 0;
    }

转载于:https://www.cnblogs.com/Paul-Guderian/p/10825318.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值