1. 程式人生 > >[CTSC2018]假面

[CTSC2018]假面

cst etc 需要 數組 type void print nbsp 此外

題目大意:
  有$n(n\le200)$個人,每個人初始血量為$m_i(m_i\le100)$對這些人進行$q(q\le2\times10^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]\times(1-p)+f[i][j+1]\times p$。特別地,對於$j=0$的情況,$f‘[i][0]=f[i][0]+f[i][1]\times p$,即,血量為$0$時,不管怎麽扣血,血量仍舊為$0$。

  考慮操作$2$,用$alive(x)$和$dead(x)$分別表示$x$在當前局面下,存活或死亡的概率。用$g[j]$表示當前集合中不包括當前封印的有$j$人存活的概率,每次把新人$x$加入到集合中時,轉移方程為$g‘[j]=alive(x)\times g[j-1]+dead(x)\times g[j]$。若封印的人為$i$,則答案為$alive(i)\times\sum_{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)\times 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," \n"[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," \n"[i==n]); 90 } 91 return 0; 92 }

[CTSC2018]假面