首页 > 代码库 > SPOJ 1825

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 33571 3 12 3 103 4 -24 5 -15 7 65 6 54 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 = http://www.mamicode.com/10 + (-2) + (-1) + 5 = 12

大意:

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

 

做法:

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

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

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

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

那么怎么做呢?

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

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

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

技术分享
  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

 

SPOJ 1825