zoukankan      html  css  js  c++  java
  • HDU5886 Tower Defence 【两遍树形dp】【最长链预处理】

    题意:N个点的一棵带权树。切掉某条边的价值为切后两树直径中的最大值。求各个边切掉后的价值和(共N-1项)。

    解法一:

    强行两遍dp,思路繁琐,维护东西较多:

    dis表示以i为根的子树的直径,dis2表示切掉以i为根的子树后的直径。

    第一遍dp,记录

      down[][0]:从i结点向下的最大距离
      down[][1]:与down[][0]无交集的向下次大距离
      dis:以i为根的子树的直径

    第二遍dp,记录

      up:从i结点向上的最远距离, 可以是w+父节点的up,也可以是w+父节点的down(判断一下down是否与w有重合)
      dis2:切掉以i为根的子树后的直径

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 #define X first
      4 #define Y second
      5 #define pii pair<int, int>
      6 #define mp make_pair
      7 typedef long long ll;
      8 const int N = 1e5+5;
      9 void gmax(int& a, int b){ if(a < b) a = b;}
     10 int n;
     11 ll ans;
     12 int down[N][2], up[N], dis[N], dis2[N];
     13 
     14 int head[N], nex[N*2], tot;
     15 pii edge[N*2];
     16 void init(){
     17     tot = 0;
     18     memset(head, -1, sizeof(head));
     19     memset(down, 0, sizeof(down));
     20     memset(up, 0, sizeof(up));
     21     memset(dis, 0, sizeof(dis));
     22     memset(dis2, 0, sizeof(dis2));
     23 }
     24 void add(int u, int v, int w){
     25     edge[tot] = mp(v, w);
     26     nex[tot] = head[u];
     27     head[u] = tot++;
     28 }
     29 //down[][0]:从i结点向下的最大距离
     30 //down[][1]:与down[][0]无交集的向下次大距离
     31 //dis:以i为根的子树的直径
     32 void dfs(int fa, int x){
     33 //    printf("dfs x %d, fa %d
    ", x, fa);
     34 //down[x][0] = down[x][1] = dis[x] = dis2[x] = up[x] = 0;
     35     for(int i = head[x]; ~i; i = nex[i]){
     36         int y = edge[i].X, w = edge[i].Y;
     37         if(y == fa) continue ;
     38         dfs(x, y);
     39         if(down[y][0]+w > down[x][0])
     40             down[x][1] = down[x][0], down[x][0] = down[y][0]+w;
     41         else if(down[y][0]+w > down[x][1])
     42             down[x][1] = down[y][0]+w;
     43         gmax(dis[x], dis[y]);
     44     }
     45     gmax(dis[x], down[x][0]+down[x][1]);
     46 }
     47 //up:从i结点向上的最远距离
     48 //dis2:切掉以i为根的子树后的直径
     49 multiset<int>::iterator it;
     50 void dfs2(int fa, int x){
     51     if(fa != -1) ans += max(dis[x], dis2[x]);
     52 //    printf("fa %d, x %d
    ", fa, x);
     53     multiset<int> se, se2;//兄弟树的直径, x往各个兄弟树的最长路
     54     for(int i = head[x]; ~i; i = nex[i]){
     55         int y = edge[i].X, w = edge[i].Y;
     56         if(y == fa) continue ;
     57         se.insert( dis[y] );
     58         se2.insert( down[y][0]+w );
     59     }
     60     for(int i = head[x]; ~i; i = nex[i]){
     61         int y = edge[i].X, w = edge[i].Y;
     62         if(y == fa) continue ;
     63         up[y] = up[x]+w;
     64         if(down[y][0]+w == down[x][0])
     65             gmax(up[y], down[x][1]+w);
     66         else gmax(up[y], down[x][0]+w);
     67         it = se.find( dis[y] ), se.erase(it);
     68         it = se2.find( down[y][0]+w ), se2.erase(it);
     69         dis2[y] = dis2[x];
     70         if(!se.empty())
     71             gmax(dis2[y], *se.rbegin());
     72         if(se2.empty()) gmax(dis2[y], up[x]);
     73         else gmax(dis2[y], up[x]+*se2.rbegin());
     74         if(se2.size() >= 2){
     75             int tmp = 0;
     76             it = se2.end();
     77             tmp += *--it;
     78             tmp += *--it;
     79             gmax(dis2[y], tmp);
     80         }
     81         dfs2(x, y);
     82         se.insert( dis[y] );
     83         se2.insert( down[y][0]+w );
     84     }
     85 }
     86 void debug(int n){
     87     for(int i = 1; i <= n; i++)
     88         printf("%d: down0 %d, down1 %d, dis %d, dis2 %d, up %d
    ", i, down[i][0], down[i][1], dis[i], dis2[i], up[i]);
     89 }
     90 int main(){
     91     int t, u, v, w; scanf("%d", &t);
     92     while(t--) {
     93         init();
     94         scanf("%d", &n);
     95         for(int i = 1; i < n; i++){
     96             scanf("%d%d%d", &u, &v, &w);
     97             add(u, v, w), add(v, u, w);
     98         }
     99         ans = 0;
    100         dfs(-1, 1);
    101         dfs2(-1, 1);
    102         printf("%lld
    ", ans);
    103     }
    104     return 0;
    105 }
    View Code

    解法二:

    先求出原树的直径。

    从直径两端分别来一次dp

    切的边不在直径上,价值为直径;

    切的边在直径上,由直径两端的dp得解。

  • 相关阅读:
    D3D中的Alpha颜色混合(1)
    最小的MFC程序
    命名空间规则【内部】
    能登陆QQ,打不开网页
    .net重要的开源组件[更新中]
    validateRequest="false"属性及xss攻击
    RSS介绍
    Virtual、Override和New关键字的使用
    SQL语句精妙集合
    绝好的软件集合
  • 原文地址:https://www.cnblogs.com/dirge/p/5975794.html
Copyright © 2011-2022 走看看