zoukankan      html  css  js  c++  java
  • SPOJ 1825

    我的树分治#3.

    After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.

    The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative (if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form a tree structure. We will choose two places as the departure and destination of the tour.

    Since September is the festival season of local inhabitants, some places are extremely crowded (we call them crowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.

    Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don't need to be different.

    Input

    There is exactly one case. First one line, containing 3 integers N K M, with 1 <= N <= 200000, 0 <= K <= M, 0 <= M <= N.

    Next M lines, each line includes an id number of a crowded place.

    The last (N - 1) lines describe (N - 1) two-way roads connected N places, form a b i, with a, b is the id of 2 places, and i is its interest value (-10000 <= i <= 10000).

    Output

    Only one number, the maximum total interest value we can obtain.

    Example

    Input: 8 2 3
    3
    5
    7
    1 3 1
    2 3 10
    3 4 -2
    4 5 -1
    5 7 6
    5 6 5
    4 8 3 Output: 12
    

    Explanation

    We choose 2 and 6 as the departure and destination place, so the tour will be 2 -> 3 -> 4 -> 5 -> 6, total interest value = 10 + (-2) + (-1) + 5 = 12

    大意:

    给你一棵树, 每个点非白即黑,求树上黑点个数不超过k的路径的最长长度.

    做法:

    我们先考虑一个笨办法. 我们只需要合并不同的子树即可. 那么必须满足子树的根节点u != v.

    根据论文上的一个好办法. u != v  <=> u < v

    也即按序合并答案. 那么我们用一个树状数组来记录前缀最大值,那么我们就能够在O(nlog2n)的时间内解决这个问题.

    SPOJ卡得非常紧,必须O(nlogn)的算法.

    那么怎么做呢?

    我们考虑将不同的子树的里面的信息合并,那么也就是保留到当前根的路径上黑点为x的最长的路径.

    然后我们不断更新这个数组,并且在更新的时候计算一个前缀最大值数组.那么我们能够在O(黑点个数)的时间内统计出一个点为根的答案.

    注意到让孩子的黑点数有序操作会提高效率!

    这是因为对于一个存在黑点路径的子树,它所需要保留的外界的路径长度都是 k-1 然后如果孩子个数不有序,那么就每次都是O(最多黑点个数)的更新复杂度....

    最坏情况下是O(n2)的.

    但是如果孩子个数有序,那么就只有之前那个孩子大小的更新复杂度.

    总的算起来,就是O(n)的.

      1 #include<cstdlib>
      2 #include<cstdio>
      3 #include<algorithm>
      4 #include<cstring>
      5 #include<cctype>
      6 #ifdef WIN32
      7 #define fmt64 "%I64d"
      8 #else
      9 #define fmt64 "%lld"
     10 #endif
     11 using namespace std;
     12 const int maxn = (int)2.5e5, inf = 0x3f3f3f3f;
     13 struct E{
     14     int t,w,last;
     15 }e[maxn * 2];
     16 int last[maxn],edg;
     17 int n,m,k;
     18 int black[maxn];
     19 void add(int x,int y,int w){
     20     e[++edg] = (E){y,w,last[x]}; last[x] = edg;
     21     e[++edg] = (E){x,w,last[y]}; last[y] = edg;
     22 }
     23 int getint(){
     24     int ret = 0,t = 1; char ch = getchar();
     25     while(!isdigit(ch)) t = ch == '-' ? -1 : 1, ch = getchar();
     26     while(isdigit(ch)) ret = ret * 10 + ch - '0', ch = getchar();
     27     return ret * t;
     28 }
     29 int tmax,node,root;
     30 int sz[maxn],vis[maxn];
     31 void getroot(int x,int fa){
     32     sz[x] = 0;
     33     int Max = 0;
     34     for(int i = last[x]; i; i = e[i].last)
     35         if(!vis[e[i].t] && e[i].t != fa){
     36             getroot(e[i].t, x);
     37             sz[x] += sz[e[i].t] + 1;
     38             Max = max(Max, sz[e[i].t] + 1);
     39         }
     40     Max = max(Max,  node - sz[x] - 1);
     41     if(tmax > Max) root = x, tmax = Max;
     42 }
     43 long long ans;
     44 int dep[maxn],path[maxn];
     45 void getpath(int x,int fa,int dis,int bal){
     46     path[bal] = max(path[bal],dis);
     47     for(int i = last[x]; i; i = e[i].last)
     48         if(!vis[e[i].t] && e[i].t != fa) getpath(e[i].t, x, dis + e[i].w, bal + black[e[i].t]);
     49 }
     50 void getdeep(int x,int fa){
     51     dep[x] = black[x];
     52     int Max = 0;
     53     for(int i = last[x]; i; i = e[i].last)
     54         if(!vis[e[i].t] && e[i].t != fa){
     55             getdeep(e[i].t, x);
     56             Max = max(Max, dep[e[i].t]);
     57         }
     58     dep[x] += Max;
     59 }
     60 int prefix[maxn],ch[maxn];
     61 int cmp(int x,int y){
     62     return dep[e[x].t] < dep[e[y].t];
     63 }
     64 void combine(int x){
     65     int size = 0,i,j,la,r,u;
     66     for(i = last[x]; i; i = e[i].last)
     67         if(!vis[e[i].t]){
     68             getdeep(e[i].t, x);
     69             ch[++size] = i;            
     70         }
     71     sort(ch + 1, ch + size + 1, cmp);    
     72     fill(prefix,prefix + dep[e[ch[size]].t] + 1, -inf);
     73     for(i = 1; i <= size; la = u, ++i){
     74         r = ch[i], u = e[r].t;
     75         fill(path, path + dep[u] + 1, -inf);
     76         getpath(u, x, e[r].w, black[u]);
     77         for(j = 0; i != 1 && j <= dep[u] && j <= k - black[x]; ++j){
     78             int pos = min(dep[la], k - j - black[x]);
     79             if(prefix[pos] == -inf) break;
     80             if(path[j] != -inf) ans = max(ans, (long long)path[j] + prefix[pos]);
     81         }
     82         for(j = 0; j <= dep[u]; ++j){
     83             prefix[j] = max(prefix[j], path[j]);
     84             prefix[j] = max(prefix[j], prefix[j-1]);
     85             if(j + black[x] <= k) ans = max(ans, (long long)prefix[j]);
     86         }
     87     }
     88 }
     89 void solve(int x){    
     90     vis[x] = 1;
     91     combine(x);
     92     for(int i = last[x]; i; i = e[i].last)
     93         if(!vis[e[i].t]){
     94             tmax = inf;
     95             node = sz[e[i].t];
     96             getroot(e[i].t, root = 0);
     97             solve(root);
     98         }
     99 }
    100 void work(){
    101     tmax = inf;
    102     node = n;
    103     getroot(1, root = 0);
    104     solve(root);
    105 }
    106 int main()
    107 {
    108     int i;
    109     n = getint(); k = getint(); m = getint();
    110     for(i = 1; i <= m; ++i){
    111         int x = getint();
    112         black[x] = 1;
    113     }
    114     for(i = 1; i < n; ++i){
    115         int a = getint(), b = getint(), w = getint();
    116         add(a,b,w);
    117     }
    118     work();
    119     printf(fmt64,ans);
    120     return 0;
    121 }
    View Code
  • 相关阅读:
    嵌入式Linux系统的构成和启动过程
    Linux 设备驱动之字符设备
    Linux的inode的理解
    flannel流程解析
    http2协议的理解
    多线程和单线程的理解
    User Token简单总结
    前端组件开发方法论
    Electron踩坑记录
    2020年工作总结
  • 原文地址:https://www.cnblogs.com/Mr-ren/p/4204490.html
Copyright © 2011-2022 走看看