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(黑点个数)的时间内统计出一个点为根的答案.

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

  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 }

时间: 2024-10-18 20:43:03

SPOJ 1825的相关文章

SPOJ 1825 FTOUR2 - Free tour II (树上点分治)

题目地址:SPOJ 1825 树分治的题果然除了模板题就是金牌题啊...这题是一道论文题,想了好长时间....终于过了,,,,注意一个坑点,如果权值全部为负的话,是可以不选任意一条边的,这样权值为0...也就是说初始值要设为0... 具体看漆子超的论文<分治算法在树的路径问题中的应用>.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue>

【SPOJ】【1825】Free Tour 2

点分治 点分治的例题2(本题代码结果为TLE……) 强烈谴责卡时限QAQ,T了无数次啊无数次…… 不过在N次的静态查错中倒是加深了对点分治的理解……也算因祸得福吧(自我安慰一下) 1 //SPOJ 1825 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n)

【BZOJ】【3611】【HEOI2014】大工程

虚树+树形DP 本题100W的点数……不用虚树真的好吗…… Orz ZYF 我的感悟: dp的过程跟SPOJ 1825 FTOUR2 的做法类似,依次枚举每个子树,从当前子树和之前的部分中各找一条最长(短)路径更新答案,再把这个子树的最短路径加入到x节点中去(之前算过的部分)这样就实现了枚举所有可能的最优情况!而且复杂度很低!避免了两两之间的枚举…… 1 /************************************************************** 2 Probl

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

SPOJ 3273

传送门: 这是一道treap的模板题,不要问我为什么一直在写模板题 依旧只放代码 1 //SPOJ 3273 2 //by Cydiater 3 //2016.8.31 4 #include <iostream> 5 #include <cstring> 6 #include <ctime> 7 #include <cmath> 8 #include <cstdlib> 9 #include <string> 10 #include

SPOJ CRAN02 - Roommate Agreement

题目链接:http://www.spoj.com/problems/CRAN02/ 题目大意:N个数字组成的序列,和为0的连续子序列的个数.N<1e6 解题思路:计算前缀和,统计每个数字出现的次数,那么对于数字sum[i], 如果存在k个sum[i],则代表有C(k, 2)个序列和为0,而如果sum[i] = 0,则还要累加上对应的k值. 代码: 1 ll n; 2 int a[maxn]; 3 ll sum[maxn]; 4 map<int, int> mmp; 5 6 void so

spoj GCJ1C09C Bribe the Prisoners

题目链接: http://www.spoj.com/problems/GCJ1C09C/ 题意: In a kingdom there are prison cells (numbered 1 to P) built to form a straight line segment. Cells number i and i+1 are adjacent, and prisoners in adjacent cells are called "neighbours." A wall wi

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问.