1. 程式人生 > >並不對勁的bzoj5340: [Ctsc2018]假面

並不對勁的bzoj5340: [Ctsc2018]假面

題目大意

\(n\)(\(n\leq200\))個非負整數\(m_1,m_2,...,m_n\)(\(\forall i\in[1,n],m_i\leq100\)),有\(q\)(\(q\leq2*10^5\))個操作,每個操作是以下兩種之一:
(1)給出位置\(x\),概率\(q\),若\(m_x\)大於0,則有\(q\)的概率將\(m_x\)減一;若\(m_x\)為0,則不進行任何操作
(2)給出一個數\(k\)\(k\)個不重複的位置\(d_1,d_2,...d_k\),在\(m_{d_1},...,m_{d_k}\)中隨機選一個正數,求選中位置\(d_1,...,d_k\)的概率各是多少,這種操作不超過1000個
最後輸出每個位置上的數在經過\(q\)

個操作後期望分別是多少。

題解

\(p_{i,j}\)表示現在第\(i\)個數還剩\(j\)的概率是多少
在進行操作前,\(p_{i,m_i}=1\),其他的\(p_{i,j}\)都為0
如果是操作(1),\(x\)\(q\)的概率減一,也就是說,\(p_{x,j}\)\(q\)的概率轉移到\(p_{x,j-1}\)\(p_{x,j}\)\(1-q\)的概率轉移到\(p_{x,j}\),特殊地,\(p_{x,0}\)\(1\)的概率轉移到\(p_{x,0}\)
如果是操作(2),設\(g_{i,j}\)表示在不考慮\(d_i\)時,有\(j\)個數是正數的概率,那麼\(d_i\)是正數且被選中的概率就是\[(1-p_{d_i,0})*(\sum_{j=0}^{k-1}g_{i,j}*\frac{1}{k+1})\]

這樣,在已知\(g\)的情況下,就可以在\(\Theta(k^2)\)的時間內求出一次(2)操作的答案了
那麼\(g\)該怎麼求呢?設\(f_{i,j}\)表示考慮位置\(d_1,...,d_i\)位置上的數,其中正數有\(j\)
初始\(f_{0,0}=1\),轉移為\[ f_{i,j} \begin{cases} f_{i-1,j-1}*(1-p_{d_i,0})+f_{i-1,j}*p_{d_i,0} & \text {j>0} \\ f_{i-1,j}*p_{d_i,0} & \text{j=0} \end{cases} \]
這樣就能求出在這\(k\)個位置中,有\(j\)
個位置上的數是正數的概率了,但是該如何把這個某個位置上的數的影響去掉,求出\(g\)呢?
重新dp一遍想必是不可能的,那樣對於每個\(d_i\)都要重新進行\(\Theta(k^2)\)的dp,一次(2)操作就是\(\Theta(k^3)\),時間超限了
能否用\(f\)\(p\)進行一些變化求出\(p\)呢?會發現在已知\(f_{i+1,0},...,f_{i+1,k}\)\(p_{d_{i+1},0}\)時,是可以求出\(f_{i,0},...,f_{i,k}\)
先把推出\(f_{i+1,0},...,f_{i+1,k}\)的所有式子寫出來:
\(f_{i+1,0}= f_{i,0}*p_{d_{i+1},0}\)
\(f_{i+1,1}= f_{i,0}*(1-p_{d_{i+1},0})+f_{i,1}*p_{d_{i+1},0}\)
\(...\)
\(f_{i+1,k}= f_{i,k-1}*(1-p_{d_{i+1},0})+f_{i,k}*p_{d_{i+1},0}\)
再把\(f_{i,0},...,f_{i,k}\)拿到左邊:
\(f_{i,0}= \frac{f_{i+1,0}}{p_{d_{i+1},0}}\)
\(f_{i,1}= \frac{f_{i+1,1}-f_{i,0}*(1-p_{d_{i+1},0})}{p_{d_{i+1},0}}\)
\(...\)
\(f_{i,k}= \frac{f_{i+1,k}-f_{i,k-1}*(1-p_{d_{i+1},0})}{p_{d_{i+1},0}}\)
特殊地,當\(p_{d_{i+1},0}=0\)時,\(f_{i,j}=f_{i+1,j+1}\)
這個變化的複雜度是\(\Theta(k)\)
\(f_{i+1,...}\)本來是\(d_1,..,d_{i+1}\)的答案,經過變化後,\(f_{i,...}\)\(d_1,..,d_{i}\)的答案,這相當於把\(d_{i+1}\)位置上的數的影響從\(f_{i+1,...}\)中去掉
那麼\(g_{i,...}\)可以看成把\(d_i\)位置上的數的影響從\(f_{n,...}\)中刪去,就可以進行相似的變化
\(p_{d_{i+1},0}\neq 0\)時,
\(g_{i,0}= \frac{f_{n,0}}{p_{d_{i+1},0}}\)
\(g_{i,1}= \frac{f_{n,1}-g_{i,0}*(1-p_{d_{i+1},0})}{p_{d_{i+1},0}}\)
\(...\)
\(g_{i,k}= \frac{f_{n,k}-g_{i,k-1}*(1-p_{d_{i+1},0})}{p_{d_{i+1},0}}\)
\(p_{d_{i+1},0}=0\)時,\(g_{i,j}=f_{g,j+1}\)

程式碼
#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 210 
#define maxm 110
#define LL long long
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*f;
}
void write(int x)
{
    if(x==0){putchar('0');return;}
    int f=0;char ch[20];
    if(x<0)putchar('-'),x=-x;
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    return;
}
const LL mod=998244353;
int inv[maxn],n,q,m[maxn],p[maxn][maxm],f[2][maxn],g[maxn],id[maxn],yes[maxn],no[maxn],num;
int qp(int x,int y){int ans=1;while(y){if(y&1)ans=(LL)ans*(LL)x%mod;x=(LL)x*(LL)x%mod,y>>=1;}return ans;}
void work()
{
    rep(i,1,num)f[0][i]=0;f[0][0]=1;
    rep(i,0,num-1)
    {
        int now=i&1,nxt=now^1;
        rep(j,0,i)
        {
            f[nxt][j+1]=((LL)f[nxt][j+1]+(LL)f[now][j]*(LL)yes[i+1]%mod)%mod;
            f[nxt][j]=((LL)f[nxt][j]+(LL)f[now][j]*(LL)no[i+1]%mod)%mod;
            f[now][j]=0;
        }
    }
    rep(i,1,num)
    {
        int res=0,fdie=qp(no[i],mod-2); 
        if(no[i]!=0)
        {
            g[0]=(LL)f[num&1][0]*(LL)fdie%mod; 
            rep(j,1,num-1)g[j]=(f[num&1][j]-(LL)g[j-1]*(LL)yes[i]%mod+mod)%mod*(LL)fdie%mod;
        }
        else
        {
            rep(j,0,num-1)g[j]=f[num&1][j+1];
        }
        rep(j,0,num-1)res=(res+(LL)yes[i]*(LL)g[j]%mod*(LL)inv[j+1]%mod)%mod;
        write(res);
        if(i!=num)putchar(' '); 
    }
    rep(i,0,num)f[num&1][i]=0;
    putchar('\n');
}
int main()
{
    n=read();
    rep(i,1,n)m[i]=read(),p[i][m[i]]=1,inv[i]=qp(i,mod-2);
    q=read();
    while(q--)
    {
        int op=read();
        if(op)
        {
            num=read();
            rep(i,1,num)id[i]=read();
            rep(i,1,num)no[i]=p[id[i]][0],yes[i]=(1-p[id[i]][0]+mod)%mod;
            work();
        }
        else
        {
            int x=read(),u=read(),v=read(),die=(LL)u*(LL)qp(v,mod-2)%mod,live=(LL)(v-u)%mod*(LL)qp(v,mod-2)%mod;
            rep(i,1,m[x])p[x][i-1]=(p[x][i-1]+(LL)p[x][i]*(LL)die%mod)%mod,p[x][i]=(LL)p[x][i]*(LL)live%mod;
        }
    }
    rep(i,1,n)
    {
        int res=0;
        rep(j,1,m[i])res=(res+(LL)j*(LL)p[i][j]%mod)%mod;
        write(res);
        if(i!=n)putchar(' ');
    }
    return 0;
}
一些意見

第一次見需要對dp結果去掉一個決策的影響的題
以及今天學會設定友鏈了!