• [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);
    }
    
  • 相关阅读:
    Mosaic 前端微服务框架
    使用skipper 扩展fabio 的路由&&http proxy 功能
    Introducing Makisu: Uber’s Fast, Reliable Docker Image Builder for Apache Mesos and Kubernetes
    lua-resty-shell 多任务执行
    openresty 使用lua-resty-shell 执行shell 脚本
    ncm 让跨项目配置一致性简单的工具
    lapis 项目添加prometheus 监控集成grafana
    使用prometheus+ grafana+nginx-module-vts 模块监控openresty
    两天快速开发一个自己的微信小程序
    笔记本如何查看mac地址
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14924619.html
Copyright © 2020-2023  润新知