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;
}
原文地址:https://www.cnblogs.com/dreagonm/p/10520844.html