我的树分治#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 }