[树形概率dp]2019徐州网络赛J Random Access Iterator

555我锅太大了,读错了A的题意(居然还做出来了)导致在A上花了太多的时间QWQ

考虑拆成子问题的话,就是我要求u访问到最深结点的概率,我就先求u的孩子v访问到最深结点的概率。

最简单的叶子结点dp[leaf]=1;

现在考虑从孩子的情况推出父亲的情况。

首先要知道,父亲走哪些孩子可以走到以父亲为根的子树的最深结点。

那么求出每个点的最深深度d[u]是多少,如果d[u]==d[v]+1,说明从v在u通往最深叶子的路上。

也就是说,如果只走一步,u走到v是有可能走到最深的,此时产生部分答案为  o=(u走到v的概率) * 【v走到最深结点的概率(就是dp[v])】

但是,假设现在u走到v1,v2都可以通往最深结点,我像上面这样计算后再求和得到的概率是有问题的。假设我现在可以走2步,可能一次走到v1,一次走到v2,这种情况本来应该只计一次数,像上面这样算就在v1,v2分别计了一次数,算重了。

所以可以求当前父亲走一步,走不到最深结点时的概率,就是1-o,走k步每步都到不了最深结点,就是(1-o)^k

那么走k步,能到最深节点的概率就是1-(1-o)^k。k其实就是当前结点的儿子个数,就是当前结点的度数-1

然后就结束了。

#include 
using namespace std;
typedef long long ll;
const ll N=2e6+7;
const ll mod=1e9+7;
ll du[N];
ll dp[N];
struct Edge{
    ll v,nxt;
}e[N];
ll p[N],edn,maxd;
void add(ll u,ll v){
    e[++edn]=(Edge){v,p[u]};p[u]=edn;
    e[++edn]=(Edge){u,p[v]};p[v]=edn;
}
ll qpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;b>>=1;
    }
    return res;
}
ll d[N],dmax[N];
ll mx;
void dfs(ll u,ll f){
    d[u]=1;
    for(ll i=p[u];~i;i=e[i].nxt){
        ll v=e[i].v;
        if(v==f) continue;
        dfs(v,u);
        d[u]=max(d[v]+1,d[u]);
    }
    if(du[u]==1) dp[u]=1;
    else{
        ll o=0;
        for(ll i=p[u];~i;i=e[i].nxt){
            ll v=e[i].v;
            if(v==f) continue;
            if(d[u]==d[v]+1){
                o+=dp[v]*qpow(du[u]-1,mod-2)%mod;
            }
        }
        o%=mod;//important
        dp[u]=1-qpow(1-o+mod,du[u]-1)+mod;
        dp[u]%=mod;
    }
}
int main() {
    ll n;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++) p[i]=-1;edn=-1;
    for(ll i=1,u,v;i

 

你可能感兴趣的:(dp,概率,DP,好题)