zoukankan      html  css  js  c++  java
  • [HDU] 2196 Computer

    http://acm.hdu.edu.cn/showproblem.php?pid=2196

    求树上每点为起点的最长路径。

    upd:最远点一定在直径的一个端点上,所以随便找一个直径,两个端点两次dfs即可(哭)

    耐心分析一下,一个点开始的最长路径,也就两种情况。

    一个是走子树,一个是走父亲那边,显然的。

    设f[i]为从点i出发走子树的最长路径,g[i]同理,为走父亲的最长路径,也就是不经过子树任何一个点的最长路径

    考虑转移:f可以通过一次dfs直接转移,f[x]=f[v]+e[i].w

    g转移就不能直接转移了,首先,g[1]=0,因为定这棵树的根为1了,不经过任何一个点的路径,当然是0。

    考虑从点x向儿子v转移,分两个情况。

    1.f[v]+e[i].w==f[x],也就是v在x向下的最长路径上

    2.v不在x向下的最长路径上

    把第一类v找出来,记为最大的儿子(树剖既视感),特殊处理。

    先做完剩下的v,既然它们不参与构成x向下的最长路径,它们就可以复用f[x](x向下的最长路径)和g[x](x向上的最长路径),有转移

    g[v]=max(f[x],g[x])+e[i].w

    然后做那个最大的v,它参与构成了f[x],考虑它向上的路径,无非有两种。

    一种是从x向上走,也就是g[x],另一种是g向下走,走次大的f[v]+e[i].w,这个次大se可以在第一类转移时顺便记录。

    于是有转移g[v]=max(g[x],se)+mxe,其中mxe是v向上的那条边,它连接了v和x。

    这就做完啦,几个小细节:多组数据,清空f和g(一开始以为下一次dfs就覆盖了,但事实是每次树的形态不同,虽然根一样,但是dfs顺序可能有差别,会错误复用一些f和g)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define int long long
    using namespace std;
    
    inline int rd() {
        int ret=0,f=1;
        char c;
        while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
        while(isdigit(c))ret=ret*10+c-'0',c=getchar();
        return ret*f;
    }
    
    const int MAXN=10005;
    
    struct Edge {
        int next,to,w;
    } e[MAXN<<1];
    int ecnt,head[MAXN];
    inline void add(int x,int y,int w) {
        e[++ecnt].next = head[x];
        e[ecnt].to = y;
        e[ecnt].w = w;
        head[x] = ecnt;
    }
    
    int n;
    int f[MAXN],g[MAXN];
    
    void F(int x,int pre) {
        for(int i=head[x]; i; i=e[i].next) {
            int v=e[i].to;
            if(v==pre) continue;
            F(v,x);
            f[x]=max(f[x],f[v]+e[i].w);
        }
    }
    
    void G(int x,int pre) {
        int mx=0,mxid=0,mxe=0;
        for(int i=head[x]; i; i=e[i].next) {
            int v=e[i].to;
            if(v==pre) continue;
            if(mx<f[v]+e[i].w) {
                mx=f[v]+e[i].w;
                mxid=v;
                mxe=e[i].w;
            }
        }
        mx=max(g[x],f[x]);
        int se=0;
        for(int i=head[x]; i; i=e[i].next) {
            int v=e[i].to;
            if(v==pre||v==mxid) continue;
            g[v]=e[i].w+mx;
            se=max(se,f[v]+e[i].w);
        }
        g[mxid]=max(g[x],se)+mxe;
        for(int i=head[x]; i; i=e[i].next) {
            int v=e[i].to;
            if(v==pre) continue;
            G(v,x);
        }
    }
    
    signed main() {
        while(~scanf("%lld",&n)) {
            ecnt=0;
            memset(head,0,sizeof(head));
            memset(f,0,sizeof(f));
            memset(g,0,sizeof(g));
            int x,y;
            for(int i=2; i<=n; i++) {
                x=rd();
                y=rd();
                add(i,x,y);
                add(x,i,y);
            }
            F(1,0);
            G(1,0);
            for(int i=1; i<=n; i++) printf("%lld
    ",max(f[i],g[i]));
        }
        return 0;
    }

    本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9332583.html

  • 相关阅读:
    快速排序
    归并排序
    堆排序
    通过先序和中序创建二叉树
    插入排序
    二叉排序树
    九宫重排
    字符串匹配 sunday算法
    傻逼数学题(math)
    最近点对学习笔记
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9332583.html
Copyright © 2011-2022 走看看