【NOIP2016提高A组集训第14场11.12】随机游走

92 篇文章 0 订阅
16 篇文章 0 订阅

Description

YJC最近在学习图的有关知识。今天,他遇到了这么一个概念:随机游走。随机游走指每次从相邻的点中随机选一个走过去,重复这样的过程若干次。YJC很聪明,他很快就学会了怎么跑随机游走。为了检验自己是不是欧洲人,他决定选一棵树,每条边边权为1,选一对点s和t,从s开始随机游走,走到t就停下,看看要走多长时间。但是在走了10000000步之后,仍然没有走到t。YJC坚信自己是欧洲人,他认为是因为他选的s和t不好,即从s走到t的期望距离太长了。于是他提出了这么一个问题:给一棵n个点的树,问所有点对(i,j)(1≤i,j≤n)中,从i走到j的期望距离的最大值是多少。YJC发现他不会做了,于是他来问你这个问题的答案。

Solution

之前不是怎么会求期望,做了这题之后会了许多。
我们设f[i]和g[i]分别为i向上走到父亲的期望值和i的父亲向下走到i的期望值。
先来看看f,设y是x的父亲,z是x的其中一个儿子,p是x的度(所以 1p 就是x走到其他点的概率)。那么 f[x]=1p+zson[x]1p(f[x]+f[z]+1) ,因为x可以直接走到y,那么就是 1p1=1p ,然后x可以走到其中的一个儿子z,概率为 1p ,步数用了1,然后从z向上走到x的期望是 f[z] ,再从x向上走到y的期望步数是 f[x] ,所以就有上面的式子。

把式子化简一下,就可以变成 1pf[x]=1+1pzson[x]f[z] ,两边同时乘以 p ,那么f[x]=p+zson[x]f[z]

我们再来看看g,假设要走到儿子u。 g[u]=1p+1pzson[x]zu(f[z]+g[u]+1)+1p(g[u]+g[x]+1) 。因为我们可以直接走到u节点,那么就是 1p1=1p ,然后我们可能会走到其他的子节点z,那么概率为 1p ,步数用了1,然后从z到x期望步数为f[z],然后再从x到u期望步数为g[u]。从x还可能往父节点y走,概率为 1p ,步数用了1,从y到x的期望步数为g[x],从x到u的期望步数为g[u]。所以就有上式。
那式子化简一下 g[u]=p+zson[x]zuf[z]+g[x] ,那么把f[x]给带进去,式子就变成了 g[u]=f[x]+g[x]f[u]

现在的问题就是怎么求最大值,我们设b[i][0]和b[i][1]分别为i这个节点向下的最大期望步数和从i的子树里面的一个节点向上走到i的最大期望步数。
那么b的转移很显然。
但是有可能b[i][0]和b[i][1]是同一个子节点转移过来的,这样直接加会有问题,所以我们还要设一个c[i][0]和c[i][1]来表示次大的期望步数,如果b的两个值是由相同的节点转移过来的话,那么就把b和c交叉匹配来更新答案。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007;
int i,j,k,l,t,n,m;
int first[maxn*2],last[maxn*2],next[maxn*2],num;
int f[maxn],g[maxn],a[maxn],b[maxn][2],c[maxn][2],ans1;
double ans;
void add(int x,int y){last[++num]=y,next[num]=first[x],first[x]=num;}
void dfs(int x,int y){
    int i;
    f[x]=a[x];
    rep(i,x){
        if(last[i]!=y){
            dfs(last[i],x);
            f[x]+=f[last[i]];
        }
    }
}
void dfs1(int x,int y){
    int i;
    rep(i,x){
        if(last[i]!=y){
            g[last[i]]=f[x]+g[x]-f[last[i]];
            dfs1(last[i],x);
        }
    }
}
void dfs2(int x,int y){
    int i;int p=0,q=0;
    rep(i,x){
        if(last[i]!=y){
            dfs2(last[i],x);
            if(b[last[i]][0]+g[last[i]]>b[x][0]){
                c[x][0]=b[x][0];
                b[x][0]=b[last[i]][0]+g[last[i]];
                p=last[i];
            }
            else if(b[last[i]][0]+g[last[i]]>c[x][0]){
                c[x][0]=b[last[i]][0]+g[last[i]];
            }
            if(b[last[i]][1]+f[last[i]]>b[x][1]){
                c[x][1]=b[x][1];
                b[x][1]=b[last[i]][1]+f[last[i]];
                q=last[i];
            }
            else if(b[last[i]][1]+f[last[i]]>c[x][1]){
                c[x][1]=b[last[i]][1]+f[last[i]];
            }
        }
    }
    if(p!=q)ans1=max(b[x][0]+b[x][1],ans1);
    else{
        ans1=max(ans1,max(c[x][1]+b[x][0],c[x][0]+b[x][1]));
    }
}
int main(){
//  freopen("rw.in","r",stdin);
//  freopen("rw.out","w",stdout);
    freopen("fan.in","r",stdin);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);
        add(k,l),add(l,k);
        a[k]++,a[l]++;
    }
    dfs(1,0);dfs1(1,0);
    dfs2(1,0);
    ans=ans1;
    printf("%.5lf\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值