zoukankan      html  css  js  c++  java
  • P3412 仓鼠找sugar II

    思路

    挺神的概率期望。。
    好吧是我太弱了,完全没有往那里想
    注意期望是具有线性性的,一条路径的期望可以变成每条边的期望求和
    概率是某件事发生的可能性,期望是某件事确定发生的代价
    首先没有终点的条件并不好做,可以转化成有终点的条件
    把根假设成终点,设f(x)是向父亲前进一步的期望移动次数(f(x)=1+sum_{vin son[x]} frac{1}{d[x]}f(v))(后面一部分是指走到子节点再走回来)
    变形得到(f(x)=d[x]+sum_{vin son[x]}f(v))(其实就是x的子树的度数和)
    (ans=sum_{i}f[i]*sz[i])
    此时可以按照每个点是根,O(n^2)的计算一遍,因为起点终点共有n^2种可能性,所以要除以n^2
    接下来考虑每个f对最后ans的贡献
    如果x是终点,则f(x)为0,产生1次贡献,sz为n
    如果终点在x的子树v中,则f(x)=totd-sumd[v],sz为n-sz[v],产生sz[v]次贡献
    如果终点不在x的子树中,则f(x)=sumd[x],sz为sz[x],产生n-sz[x]次贡献
    可以脑补一下以x为分界点,把两侧的路径拼合的过程

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    #define int long long
    const int MOD = 998244353;
    int f[100100],d[100100],totd,sumd[100100],sz[100100];
    int u[100100<<1],v[100100<<1],fir[100100],nxt[100100<<1],cnt,ans;
    int n;
    int pow(int a,int b){
        int ans=1;
        while(b){
            if(b&1)
                ans=(ans*a)%MOD;
            a=(a*a)%MOD;
            b>>=1;
        }
        return ans;
    }
    void addedge(int ui,int vi){
        ++cnt;
        u[cnt]=ui;
        v[cnt]=vi;
        nxt[cnt]=fir[ui];
        fir[ui]=cnt;
    }
    void dfs1(int u,int f){
        sz[u]=1;
        if(f)
            d[u]=1;
        for(int i=fir[u];i;i=nxt[i]){
            if(v[i]==f)
                continue; 
            d[u]++;
            dfs1(v[i],u);
            sz[u]+=sz[v[i]];
            sumd[u]+=sumd[v[i]];
        }
        sumd[u]+=d[u];
        totd+=d[u];
    }
    void dfs2(int u,int f){
        for(int i=fir[u];i;i=nxt[i]){
            if(v[i]==f){
                ans=(ans+sumd[u]*sz[u]%MOD*(n-sz[u])%MOD)%MOD;
                continue;
            }
            ans=(ans+(totd-sumd[v[i]])*(n-sz[v[i]])%MOD*sz[v[i]]%MOD)%MOD;
            dfs2(v[i],u);
        }
    }
    signed main(){
        freopen("std.in","r",stdin);
        scanf("%lld",&n);
        for(int i=1;i<n;i++){
            int a,b;
            scanf("%lld %lld",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }
        dfs1(1,0);
        // printf("%lld
    ",totd);
        dfs2(1,0);
        printf("%lld
    ",ans*pow(n*n%MOD,MOD-2)%MOD);
        return 0;
    }
    
  • 相关阅读:
    Android研究之游戏开发处理按键的响应
    C语言指针的初始化和赋值
    Cloudera CDH 5集群搭建(yum 方式)
    未将对象引用设置到对象的实例--可能出现的问题总结
    内存泄漏以及常见的解决方法
    都能看懂的嵌入式linux/android alsa_aplay alsa_amixer命令行使用方法
    Life is hard!
    EasyUI基础入门之Resiable(可缩放)
    Android -- Looper.prepare()和Looper.loop() —深入版
    vi 命令 使用方法
  • 原文地址:https://www.cnblogs.com/dreagonm/p/10520844.html
Copyright © 2011-2022 走看看