1. 程式人生 > 其它 >[CTS2019]氪金手遊

[CTS2019]氪金手遊

Solution

先考慮外向樹的情況,這樣根一定是最小的,所有子節點都必須比它後訪問,而所有非子節點多久抽到無所謂。假定每個點的 \(w_i\) 是定值,\(s_i\)\(i\) 的子樹和,\(S\) 是總和,那麼概率就是

\[\prod_{i=1}^n \frac{w_i}{S} \sum_{j=0}^{\infty} (\frac{S-s_i}{S})^{j}=\prod_{i=1}^n \frac{w_i}{s_i} \]

發先最後答案只和每個點的 \(w\)\(s\) 有關。考慮把 \(w\)\(s\) 壓入狀態進行 dp,然後發現 \(w\) 可以去掉。記 \(dp[u][x]\)

表示考慮 \(u\) 這個子樹, \(w_i\) 和為 \(x\) 且符合關係的概率。這樣就得到一個 \(O(n^2)\) 的 dp。

再考慮加入反向邊,一個容斥小技巧,將反向邊拆成不考慮這條邊(分成兩個連通塊),減去這條邊是正向邊的情況。後者就是外向樹的情況,前者將其分成兩個連通塊,容易發現這兩個連通塊互不干擾,所以概率就是兩個分別 dp 後的乘積,也即一個外向樹森林。那麼最後的概率就可以通過列舉每條反向邊的方向,然後 dp,再乘上一個容斥係數,求和。複雜度 \(O(2^n n^2)\)

又發現我們並不在意每條邊具體是什麼方向,容斥係數只和反向邊沒有反向的數量有關,而 dp 轉移只和 \(w\)

\(s\) 有關。所以考慮轉移的時候如果是反向邊就將概率乘上負一,由分配率知這樣是對的。

#include<stdio.h>

const int N=1e3+7;
const int M=3e6+7;
const int Mod=998244353;

struct E{
    int next,to;
    bool tag;
}e[N<<1];

int head[N],cnt=0,n;
int inv[M],sz[N],dp[N][N*3],tmp[N*3];

inline void add(int id,int to){
    e[++cnt]=(E){head[id],to,0};
    head[id]=cnt;
    e[++cnt]=(E){head[to],id,1};
    head[to]=cnt;
}

void dfs(int u,int fa){
    sz[u]=1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(fa==v) continue;
        dfs(v,u);
        for(int x=1;x<=sz[u]*3;x++)
            for(int y=1;y<=sz[v]*3;y++){
                int t=1ll*dp[u][x]*dp[v][y]%Mod;
                if(e[i].tag) tmp[x+y]=(tmp[x+y]-t+Mod)%Mod,tmp[x]=(tmp[x]+t)%Mod;
                else tmp[x+y]=(tmp[x+y]+t)%Mod;
            }
        sz[u]+=sz[v];
        for(int x=1;x<=sz[u]*3;x++) dp[u][x]=tmp[x],tmp[x]=0;
    }
    for(int i=1;i<=sz[u]*3;i++) dp[u][i]=1ll*dp[u][i]*inv[i]%Mod;
}

int main(){
    scanf("%d",&n); inv[1]=1;
    for(int i=2;i<M;i++)
        inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
    for(int i=1,a,b,c;i<=n;i++){
        scanf("%d%d%d",&a,&b,&c);
        int w=inv[a+b+c];
        dp[i][1]=1ll*a*w%Mod;
        dp[i][2]=1ll*b*w%Mod*2ll%Mod;
        dp[i][3]=1ll*c*w%Mod*3ll%Mod;
    }
    for(int i=1,u,v;i<n;i++)
        scanf("%d%d",&u,&v),add(u,v);
    dfs(1,0); int ans=0;
    for(int i=1;i<=3*n;i++) ans=(ans+dp[1][i])%Mod;
    printf("%d",ans);
}