[CTS2019]氪金手游

Posted wwlw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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);
}

以上是关于[CTS2019]氪金手游的主要内容,如果未能解决你的问题,请参考以下文章

p5405 [CTS2019]氪金手游

[CTS2019]氪金手游(容斥+树形背包DP)

Luogu5405 CTS2019氪金手游(容斥原理+树形dp)

Hello the world

龙威零式-团队博客

龙威零式-团队博客