LOJ 2552 「CTSC2018」假面——DP

题目:https://loj.ac/problem/2552

70 分就是 f[i][j] 表示第 i 个人血量为 j 的概率。这部分是 O( n*Q ) 的;g[i][j][0/1] 表示询问的人中,前/后 i 个人,存活 j 个人的概率。做 g[ ][ ] 是 n^2 的,算答案是 n3 的。

考虑 g[ i ] 表示询问的人中有 i 个存活的概率。因为每个人加入 g[ ] 的顺序无关,所以可以每次 O(n) 地从g[ ] 里剔除第 i 个人的贡献。

令第 i 个人不存活的概率是 u ,存活的概率是 v 。

当初的转移是 g[ i ] * u -> g'[ i ] , g[ i ] * v -> g'[ i+1 ] ,所以现在可以倒着做:g[ i ] = g'[ i+1 ] / v , g'[ i ] -= g[ i ] ,或者正着做:g[ i ] = g'[ i ] / u , g'[ i+1 ] -= g[ i ] * v 。

发现如果 u=0 ,那么 g[ i ] 的值只体现在了 g'[ i+1 ] 里;所以倒着做。如果 v=0 就正着做。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
const int N=205,M=105,mod=998244353;
int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}
int pw(int x,int k)
{int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}

int n,a[N],f[N][M],g[N],tp[N],h[N],q[N],inv[N];
int main()
{
  n=rdn(); inv[1]=1;
  for(int i=2;i<=n;i++)inv[i]=(ll)upt(-mod/i)*inv[mod%i]%mod;
  for(int i=1,d;i<=n;i++)
    { a[i]=rdn();f[i][a[i]]=1;}
  int Q=rdn(),op,x,u,v;
  while(Q--)
    {
      op=rdn();
      if(!op)
    {
      x=rdn();u=rdn();v=rdn();
      u=(ll)u*pw(v,mod-2)%mod;
      for(int j=1;j<=a[x];j++)
        {
          f[x][j-1]=(f[x][j-1]+(ll)f[x][j]*u)%mod;
          f[x][j]=(ll)f[x][j]*upt(1-u)%mod;
        }
      if(!f[x][a[x]])a[x]--;
    }
      else
    {
      x=rdn(); int tl=0;
      for(int i=1;i<=x;i++)
        q[++tl]=rdn();
      g[0]=1;for(int i=1;i<=tl;i++)g[i]=0;
      for(int i=1;i<=tl;i++)
        {
          u=f[q[i]][0]; v=upt(1-u);
          for(int j=i;j>=0;j--)
        {
          g[j]=(ll)g[j]*u%mod;
          if(j)g[j]=(g[j]+(ll)g[j-1]*v)%mod;
        }
        }
      for(int i=1;i<=tl;i++)
        {
          u=f[q[i]][0]; v=upt(1-u);
          memcpy(tp,g,sizeof g);
          if(!u)
        {
          int iv=pw(v,mod-2);
          for(int j=tl-1;j>=0;j--)
            {
              h[j]=(ll)tp[j+1]*iv%mod;
              tp[j]=upt((tp[j]-(ll)u*h[j])%mod);
            }
        }
          else
        {
          int iu=pw(u,mod-2);
          for(int j=0;j<tl;j++)
            {
              h[j]=(ll)tp[j]*iu%mod;
              tp[j+1]=upt((tp[j+1]-(ll)v*h[j])%mod);
            }
        }
          int ans=0;
          for(int j=0;j<tl;j++)
        ans=(ans+(ll)h[j]*v%mod*inv[j+1])%mod;
          printf("%d ",ans);
        }
      puts("");
    }
    }
  for(int i=1;i<=n;i++)
    {
      int ans=0;
      for(int j=0;j<=a[i];j++)
    ans=(ans+(ll)j*f[i][j])%mod;
      printf("%d ",ans);
    }
  puts(""); return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/10823577.html