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 
* Added some unofficial cases

题意:给定一棵边权树,树上的点有M个为黑色,其余为白色。现在求简单路径的最大权,满足路径上的黑点不超过K个。

思路:不难想到是分治题。把问题转化为子问题:求经过根节点的不超过K个黑点的最大权路径。然后对于每一个子问题,像树形DP那样乱搞就行。但是每个子问题的解决需要线性,才能得到NlogN的复杂度。此题的难点就是如何线性得到根的下面的若干个子树的信息。

具体的,我们把问题转化为若干棵树,每棵树把重心转化为根节点root;用dis[i]数组,表示从根节点向下经过i个黑色节点的最大权值。假设root下面有X个子树,对于当前前面的S个子树,我们已经维护好了这S个子树的dis数组。(A):对于第S+1个子树,我们用tdis[j]数组表示第S+1棵子树经过j个黑色节点的最大权值;对所有i+j<=K,dis[i]+tdis[j]来更新ans,这里可以利用更新前缀: dis[i]=min(dis[i],dis[i-1]) 来降低复杂度到线性。(B):而访问完第S+1棵树后,又用tdis数组去更新dis数组:dis[i]=min(dis[i], tdis[i]);  对于A部分,是线性的;而B部分,取决于子树X的多少,最坏可以到达N^2。  所以必须降低更新S部分,即每棵子树信息的复杂度。解决方案是启发式合并。   即每一层排序后处理,而每个点只有一次参加排序,所以排序复杂度也是O(NlgN)的。

没有优化的代码,A,B:超时。

#include<bits/stdc++.h>
using namespace std;
const int maxn=;
const int inf=1e9+;
int Laxt[maxn],Next[maxn],To[maxn],cost[maxn],B[maxn],cnt,N,M,K;
int dis[maxn],tdis[maxn],sz[maxn],son[maxn],vis[maxn],sn,root,ans;
void add(int u,int v,int d){
Next[++cnt]=Laxt[u];
Laxt[u]=cnt;
To[cnt]=v; cost[cnt]=d;
}
void getroot(int u,int fa)
{
sz[u]=; son[u]=;
for(int i=Laxt[u];i;i=Next[i]){
if(To[i]!=fa&&!vis[To[i]]){
getroot(To[i],u);
sz[u]+=sz[To[i]];
son[u]=max(son[u],sz[To[i]]);
}
}
son[u]=max(son[u],sn-son[u]);
if(root==||son[root]>son[u]) root=u;
}
void solve(int u,int fa,int used,int sum)
{
if(used>K) return ;
tdis[used]=max(tdis[used],sum);
ans=max(ans,sum+dis[K-used]);
for(int i=Laxt[u];i;i=Next[i]){
if(!vis[To[i]]&&To[i]!=fa)
solve(To[i],u,used+B[To[i]],sum+cost[i]);
}
}
void dfs(int u)
{
vis[u]=;
dis[]=; for(int i=;i<=K;i++) dis[i]=-inf; //A
if(B[u]) K--;
for(int i=Laxt[u];i;i=Next[i])
if(!vis[To[i]]){
for(int j=;j<=K;j++) tdis[j]=-inf; //B
solve(To[i],,B[To[i]],cost[i]); //D线性部分
dis[]=max(dis[],tdis[]);
for(int j=;j<=K;j++){ //C
dis[j]=max(dis[j],dis[j-]);
dis[j]=max(dis[j],tdis[j]);
ans=max(ans,dis[j]);
}
}
if(B[u]) K++;
for(int i=Laxt[u];i;i=Next[i]){
if(vis[To[i]]) continue;
root=; sn=sz[To[i]];
getroot(To[i],); dfs(root);
}
}
int main()
{
int u,v,x;
scanf("%d%d%d",&N,&K,&M);
for(int i=;i<=M;i++) scanf("%d",&x),B[x]=;
for(int i=;i<N;i++){
scanf("%d%d%d",&u,&v,&x);
add(u,v,x); add(v,u,x);
}
root=; sn=N; getroot(,); dfs(root);
printf("%d\n",ans);
return ;
}

优化的代码,B部分通过启发式合并。每次我们得到最深的深度,然后更新部分tdis数组只更新到最深的地方。

#include<bits/stdc++.h>。
using namespace std;
const int maxn=;
const int inf=0x7FFFFFFF;
int Laxt[maxn],Next[maxn<<],To[maxn<<],cost[maxn<<],B[maxn],cnt,N,M,K;
int dis[maxn],tdis[maxn],sz[maxn],son[maxn],vis[maxn],sn,root,ans;
struct in{ int dep,id; }s[maxn];
bool cmp(in w,in v){ return w.dep<v.dep; }
void read(int &x){
x=; int f=; char c=getchar();
while(c>''||c<'') { if(c=='-') f=-;c=getchar();}
while(c<=''&&c>='') x=(x<<)+(x<<)+c-'',c=getchar();
x*=f;
}
void add(int u,int v,int d){
Next[++cnt]=Laxt[u];
Laxt[u]=cnt;
To[cnt]=v; cost[cnt]=d;
}
void getroot(int u,int fa)
{
sz[u]=; son[u]=;
for(int i=Laxt[u];i;i=Next[i]){
if(To[i]!=fa&&!vis[To[i]]){
getroot(To[i],u);
sz[u]+=sz[To[i]];
son[u]=max(son[u],sz[To[i]]);
}
}
son[u]=max(son[u],sn-son[u]);
if(root==||son[root]>son[u]) root=u;
}
int getmaxdep(int u,int fa,int used)
{
if(used==K) return K;
int res=used;
for(int i=Laxt[u];i;i=Next[i]){
if(!vis[To[i]]&&To[i]!=fa){
res=max(res,getmaxdep(To[i],u,used+B[To[i]]));
}
}return res;
}
void solve(int u,int fa,int used,int sum)
{
if(used>K) return ;
tdis[used]=max(tdis[used],sum);
for(int i=Laxt[u];i;i=Next[i]){
if(!vis[To[i]]&&To[i]!=fa)
solve(To[i],u,used+B[To[i]],sum+cost[i]);
}
}
void dfs(int u)
{
vis[u]=; dis[]=;
if(B[u]) K--;
int tot=;
for(int i=Laxt[u];i;i=Next[i])
if(!vis[To[i]]){
tot++; s[tot].id=i;
s[tot].dep=getmaxdep(To[i],u,B[To[i]]);
}
sort(s+,s+tot+,cmp);
for(int i=;i<=tot;i++){ //这里优化的核心
solve(To[s[i].id],u,B[To[s[i].id]],cost[s[i].id]);
if(i!=) {
for(int j=;j<=s[i-].dep;j++) dis[j]=max(dis[j],dis[j-]);
for(int j=s[i].dep;j>=;j--) ans=max(ans,dis[min(s[i-].dep,K-j)]+tdis[j]);
//这里注意min一下,因为二者之和不一定能打到K,因为这里WA了很多次。
}
for(int j=;j<=s[i].dep;j++) dis[j]=max(dis[j],tdis[j]),tdis[j]=;
}
for(int j=;j<=s[tot].dep;j++){
ans=max(ans,max(dis[j],tdis[j]));
dis[j]=tdis[j]=;
}
if(B[u]) K++;
for(int i=Laxt[u];i;i=Next[i]){
if(vis[To[i]]) continue;
root=; sn=sz[To[i]];
getroot(To[i],); dfs(root);
}
}
int main()
{
int u,v,x;
scanf("%d%d%d",&N,&K,&M);
for(int i=;i<=M;i++) read(x),B[x]=;
for(int i=;i<N;i++){
read(u); read(v); read(x);
add(u,v,x); add(v,u,x);
}
root=; sn=N; getroot(,); dfs(root);
printf("%d\n",ans);
return ;
}

SPOJ:Free tour II (树分治+启发式合并)的更多相关文章

  1. SP1825 FTOUR2 - Free tour II 点分治+启发式合并+未调完

    题意翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Code: #include <bits/stdc++.h> using n ...

  2. [spoj] FTOUR2 FREE TOUR II || 树分治

    原题 给出一颗有n个点的树,其中有M个点是拥挤的,请选出一条最多包含k个拥挤的点的路径使得经过的权值和最大. 正常树分治,每次处理路径,更新答案. 计算每棵子树的deep(本题以经过拥挤节点个数作为d ...

  3. SPOJ 1825 Free tour II 树分治

    题意: 给出一颗边带权的数,树上的点有黑色和白色.求一条长度最大且黑色节点不超过k个的最长路径,输出最长的长度. 分析: 说一下题目的坑点: 定义递归函数的前面要加inline,否则会RE.不知道这是 ...

  4. Problem E. TeaTree - HDU - 6430 (树的启发式合并)

    题意 有一棵树,每个节点有一个权值. 任何两个不同的节点都会把他们权值的\(gcd\)告诉他们的\(LCA\)节点.问每个节点被告诉的最大的数. 题解 第一次接触到树的启发式合并. 用一个set维护每 ...

  5. SPOJ Free TourII(点分治+启发式合并)

    After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, ...

  6. bzoj2733 永无乡 splay树的启发式合并

    https://vjudge.net/problem/HYSBZ-2733 给一些带权点,有些点是互相连通的, 然后给出2种操作,在两点间加一条边,或者询问一个点所在的连通块内的第k小值的编号 并查集 ...

  7. 【BZOJ3123】森林(主席树,启发式合并)

    题意:一个带点权的森林,要求维护以下操作: 1.询问路径上的点权K大值 2.两点之间连边 n,m<=80000 思路:如果树的结构不发生变化只需要维护DFS序 现在因为树的结构发生变化,要将两棵 ...

  8. BZOJ3123[Sdoi2013]森林——主席树+LCA+启发式合并

    题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

  9. DSU模板(树的启发式合并)

    摘自Codeforces博客 With dsu on tree we can answer queries of this type: How many vertices in subtree of ...

随机推荐

  1. android本地存储SharedPreferences

    SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对)SharedPreferences常用来 ...

  2. AC日记——美元汇率 洛谷 P1988

    题目背景 此处省略maxint+1个数 题目描述 在以后的若干天里戴维将学习美元与德国马克的汇率.编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值. 输入输出格式 ...

  3. codevs——2152 滑雪

    2152 滑雪  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description trs喜欢滑雪.他来到了一个滑雪场,这个滑雪场 ...

  4. OpenCV、PCL;Xtion、kinect;OpenNI、kinect for windows SDK比较

    一.对比介绍: 1. OpenCV:开源跨平台,OpenCV于1999年由Intel建立,如今由Willow Garage提供支持. 2. OpenNI:OpenNI组织创建于2010年11月.主要成 ...

  5. mysql delete语句不能用别名

    在mysql数据库里运行delete语句 delete ’; 发现会报错: [Err] - You have an error in your SQL syntax; check the manual ...

  6. jquery+css实现邮箱自动补全

    今天在公司做一个电子商务网站的注册会员时,要求用户在电子邮箱文本框中输入时,给与热点提示常用的电子邮箱,帮助用户选择,提高体验效果.下面是用Jquery+css实现的邮箱自动补全,供大家参考和学习. ...

  7. Docker资源限制实现——cgroup

    摘要 随着Docker技术被越来越多的个人.企业所接受,其用途也越来越广泛.Docker资源管理包含对CPU.内存.IO等资源的限制,但大部分Docker使用者在使用资源管理接口时往往还比较模糊. 本 ...

  8. 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&构建使用jquery来实现

    使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&构建使用jquery来实现 1.首先你自己把webstorm安装完成. 请参考这篇文章进行安装和破解 ...

  9. ubuntu 16.04 更新后搜狗输入法无法输入中文的问题

    方法一:重启搜狗输入法 通过下面的两个命令重启搜狗输入法,看重启后是否可以正常使用: ~$ killall fcitx  ~$ killall sogou-qinpanel   方法二:检查修复安装依 ...

  10. 拒绝干扰 解决Wi-Fi的最大问题

    本文转载至:http://www.ciotimes.com/net/rdjs/WI-FI/201006301920.html 射频干扰英文:RFI,(Radio Frequency Interfere ...