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

9 篇文章 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]表示第i个点往上走到自己父亲的期望步数,那么 f[x]=yson[x]f[y]+f[x]+1|son[x]|+1+1|son[x]|+1 ,( yson[x]f[y]+f[x]+1|son[x]|+1 表示x先走到x的儿子后再走回x)化简得 f[x]=|son[x]|+1+yson[x]f[y] 。我们设e(i,j)表示j的父亲i往下走到j的期望步数。那么 e(x,y)=zson[x]1+f[z]+e(x,y)|son[x]|+1+1|son[x]|+1+e(fa[x],x)+1+e(x,y)|son[x]|+1 。解释一下: zson[x]1+f[z]+e(x,y)|son[x]|+1 表示x走到x别的儿子再走回x,最后走到y; e(fa[x],x)+1+e(x,y)|son[x]|+1 表示x走到自己的父亲后再走下来到x; 1|son[x]|+1 表示x一步走到y。化简得 e(x,y)=zson[x]f[z]+e(fa[x],x)+|son[x]|+1 。然后我们记g[y]=e(x,y)。

最后我们设f[x][0..1]分别表示从下往上走到x和x往下走的最大值,做一下树形dp取最大值即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define db double
using namespace std;
const int maxn=200005;
int first[maxn],last[maxn],next[maxn],e[maxn],g[maxn],size[maxn],f[maxn][2];
int n,i,t,j,k,l,num,x,y,ans;
db ans1;
void lian(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void dg(int x,int y){
    int t;
    e[x]=size[x];
    for (t=first[x];t;t=next[t]){
        if (last[t]==y) continue;
        dg(last[t],x);
        e[x]+=e[last[t]];
    }
}
void dg1(int x,int y){
    int t;
    for (t=first[x];t;t=next[t]){
        if (last[t]==y) continue;
        g[last[t]]=e[x]-e[last[t]]+g[x];
        dg1(last[t],x);
    }
}
void dg2(int x,int y){
    int t,p,q,p1,q1;p=q=p1=q1=0;
    for (t=first[x];t;t=next[t]){
        if (last[t]==y) continue;
        dg2(last[t],x);
        f[x][0]=max(f[x][0],f[last[t]][0]+g[last[t]]);
        f[x][1]=max(f[x][1],f[last[t]][1]+e[last[t]]);
        if (f[last[t]][0]+g[last[t]]>f[p][0]+g[p] || !p) p1=p,p=last[t];
        else if (f[last[t]][0]+g[last[t]]>f[p1][0]+g[p1] || !p1) p1=last[t];
        if (f[last[t]][1]+e[last[t]]>f[q][1]+e[q] || !q) q1=q,q=last[t];
        else if (f[last[t]][1]+e[last[t]]>f[q1][1]+e[q1] || !q1) q1=last[t];
    }
    if (p!=q) ans=max(ans,f[p][0]+g[p]+f[q][1]+e[q]);
    else ans=max(ans,max(f[p][0]+f[q1][1]+g[p]+e[q1],f[p1][0]+g[p1]+f[q][1]+e[q]));
}
int main(){
    freopen("rw.in","r",stdin);freopen("rw.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<n;i++)
        scanf("%d%d",&x,&y),lian(x,y),lian(y,x),size[x]++,size[y]++;
    dg(1,0);
    dg1(1,0);
    dg2(1,0);
    ans1=ans;
    printf("%.5lf\n",ans1);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值