SPOJ1825 FTOUR2 - Free tour II

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

Description

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 atree 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 themcrowded 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

正解:树的点分治

解题报告:

  这道题需要统计树上经过黑点数量<=k的路径最长长度。

  这种树上的一类路经统计,很快能想到用树分治来做。
  考虑当$u$作为根时,我们仅统计经过$u$的所有路径,其余的递归处理。设$G[i]$表示之前的所有子树中经过黑点数量$<=i$的路径最长长度,$dep[i]$表示子节点$i$到其子树中经过的最大黑点数量。

  那么,我们可以枚举这次$dfs$的子节点,经过了多少个黑点$j$,那么可以与之组合的就是$G[k-black[u]-j]$,如果$<0$则跳过。

  容易发现我们每次统计完一棵子树之后,需要重新更新G,而G[i-1]又需要用G[i]更新,所以统计完这棵子树时,更新$G$和统计答案的复杂度是之前处理的所有节点的$max(dep[i])$,最坏情况可以变成每次都是$n^2$。

  考虑统计顺序不影响最终结果,那么我们可以按$dep[i]$排序,从小到大处理,则所有子树的$dep$之和显然$<=n$。

  总复杂度$O(nlog^2 n)$

  ps:我还是不是很懂为什么这组数据答案是0:

  1 0 1 
  1  

  

  这组数据应该没有合法的路径吧...开始一直WA,我调了好久结果特判掉这个点就过了...

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXN = 200011;
const int MAXM = 400011;
const int inf = (1<<30)-1;
int n,k,m,ecnt,first[MAXN],next[MAXM],to[MAXM],w[MAXM],black[MAXN];
bool use[MAXN];
int ans,tot,size[MAXN],S,nowL,nowg[MAXN];
int ansL,G[MAXN],maxd,dep[MAXN];//dep[x]表示x到子树内的所有节点中路径上黑点最多的数量
struct Tree{ int dep,x,z; }b[MAXN];
inline bool cmp(Tree q,Tree qq){ return q.dep<qq.dep; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

inline void dfs(int x,int fa){
	size[x]=1; tot++;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]) continue;
		dfs(v,x); size[x]+=size[v];
	}
}

inline void dfs2(int u,int fa,int &rt){
	int maxl=0;
	for(int i=first[u];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]/*!!!*/) continue;
		dfs2(v,u,rt); maxl=max(maxl,size[v]);
	}
	if(tot-size[u]>maxl) maxl=tot-size[u];
	if(rt==-1) S=maxl,rt=u;
	else if(maxl<S) { S=maxl; rt=u; }
}

inline void find_root(int u,int &rt){
	tot=0; dfs(u,0); S=0;
	dfs2(u/*!!!*/,0,rt); if(tot==1) rt=-1;
}

inline void getdep(int x,int fa,int dd){
	if(black[x]) dd++; if(dd>maxd) maxd=dd;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]/*!!!*/) continue;
		getdep(v,x,dd);
	}
}

inline void dfs3(int x,int fa,int dep,int dd){
	if(black[x]) dep++;

	if(dep>nowL) { nowL=dep; nowg[dep]=dd; }
	else nowg[dep]=max(nowg[dep],dd);

	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || use[v]/*!!!*/) continue;
		dfs3(v,x,dep,dd+w[i]);
	}
}

inline void solve(int u){
	int rt=-1; find_root(u,rt);
	if(rt==-1) return ; int cnt=0;
	u=rt; int now;
	for(int i=first[u];i;i=next[i]) {
		int v=to[i]; if(use[v]) continue;
		maxd=0; getdep(v,u,0); dep[v]=maxd;
		b[++cnt].x=v; b[cnt].dep=dep[v]; b[cnt].z=w[i];
	}
	sort(b+1,b+cnt+1,cmp); ansL=-1;
	for(int o=1;o<=cnt;o++) {
		int v=b[o].x; nowL=-1;
		nowg[0]=-inf;//!!!
		dfs3(v,u,0,b[o].z);
		for(int i=1;i<=dep[v];i++) nowg[i]=max(nowg[i-1],nowg[i]);
		if(ansL>=0) {//查询之前的最优值
			for(int i=0;i<=dep[v];i++) {
				if(nowg[i]==-inf) continue;
				now=k-black[u]-i; if(now<0) break;
				if(now>ansL) now=ansL;//!!!
				ans=max(ans,G[now]+nowg[i]);
			}
		}
		for(int i=0;i<=ansL;i++) G[i]=max(G[i],nowg[i]);
		for(int i=nowL;i>ansL;i--) G[i]=nowg[i];
		ansL=nowL;
		for(int i=1;i<=ansL;i++) G[i]=max(G[i],G[i-1]);
	}
	for(int i=0;i<=ansL;i++) if(i+black[u]<=k) ans=max(ans,G[i]); else break;

	use[u]=1;
	for(int i=first[u];i;i=next[i]) {//!!!
		int v=to[i]; if(use[v]) continue;
		solve(v);
	}
}

inline void work(){
	n=getint(); k=getint(); m=getint(); int x,y,z;
	for(int i=1;i<=m;i++) { x=getint(); black[x]=1; }
	for(int i=1;i<n;i++) {
		x=getint(); y=getint(); z=getint();
		next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
		next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
	}
	ans=-inf; for(int i=0;i<=n;i++) G[i]=-inf;
	solve(1);
	if(ans==-inf) {
		ans=0;
		/*for(int i=1;i<=n;i++)
		  if(black[i]<=k) { ans=0; break; }*/
	}
	printf("%d",ans);
}

int main()
{
    work();
    return 0;
}

  

时间: 2024-10-06 08:25:34

SPOJ1825 FTOUR2 - Free tour II的相关文章

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

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

SPOJ1825:Free tour II

题意 luogu的翻译 给定一棵n个点的树,树上有m个黑点,求出一条路径,使得这条路径经过的黑点数小于等于k,且路径长度最大 Sol 点分治辣 如果是等于\(k\)的话,开个桶取\(max\)就好了 而小于等于\(k\),就可以把桶换成树状数组,求前缀\(max\) 很慢能过 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a))

SP1825 【FTOUR2 - Free tour II】

# \(SP1825\) 看到没有人用老师的办法,于是自己写一下思路 思路第一步:排除旧方法 首先这道题和\(4178\)不一样,因为那道题是计数,而这道题是求最值,最值有个坏处,就是对于来自相同子树的信息没法高效剔除,比如容斥用不了,举例来说,对于这道题,如果我们继续用尺取法维护黑点个数,对于一组刚好使\(cnt_l+cnt_r\leq k\)的\(l,r\),且\([l+1,r]\)所在子树都与\(l\)不同时就可以选取\(l~and~x\in [l+1,r]\),我们要使\(dis_l+d

SPOJ1825/FTOUR2:Free tour II——包看得懂/看不懂题解

http://www.spoj.com/problems/FTOUR2/en/ 题目大意:给一棵黑白染色的树,求边权和最大且经过黑点不超过K的路径. ———————————————————— 前排膜拜hzwer,借(抄)鉴(袭)了神犇的代码与思路,要看简洁的思路的话:http://hzwer.com/5984.html 然而……就算是抄了代码,还是没懂这题怎么做怎么办? 别着急,这道神神神题慢慢来. —————————————————————— 首先判断我们要放什么板子,很显然我们想到的是和树有

SPOJ1825 Free tour II 树分治

题意:带边权树上有白点和黑点,问你最多不经过k个黑点使得路径最长(注意,路径有负数) 解题思路:基于树的点分治.数的路径问题,具体看09QZC论文,特别注意 当根为黑时的情况 解题代码: 1 // File Name: spoj1825.cpp 2 // Author: darkdream 3 // Created Time: 2014年10月05日 星期日 20时20分33秒 4 5 #include<vector> 6 #include<list> 7 #include<

spoj1825 Free tour II

题目链接 一道神奇的点分治 貌似有很多做法,我觉得BIT要好些一些(雾 要求经过黑点数<k就用BIT区间查询前缀 对于每个点用  BIT[0,k-经过黑点数]的最大值+路径长度 使用点分治做到O(n*log22n) 貌似还有O(nlog2n)的做法(雾 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cstd

【BZOJ】1825. Free tour II(点分治)

http://www.spoj.com/problems/FTOUR2/ 先前看了一会题解就自己yy出来了...对拍过后交tle.................. 自己造了下大数据........tle......................... what? 明早更新吧..调到吐了.. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include &l

[SP1825] Free tour II

/* ----------------------- [题解] https://www.luogu.org/blog/IRving1-1/solution-sp1825 ----------------------- O(Nlog^2)做法,vjudge上写的是时限100ms,过2e5数据 ----------------------- 统计tmp[i]为有i个黑点的最长路径,进行转移 合并的顺序很巧妙,也很重要,这里倒序枚举当前子树的j(tmp[j]),则可以做到控制维护之前子树cur(max

[SPOJ1825]FTOUR2

我们知道,树上两个点的LCA要么是当前根节点,要么不是..所以两个点间的最短路径要么经过当前根节点,要么在一棵当前根节点的子树中.. 考虑点分治,于是在原来同一子树中的两个点必然在一次分治中变为路径经过当前根节点的两个点. 点分治标准开头(雾 对于路径经过当前根节点的点.从当前根到点\(i\)的路径上经过的拥挤点数为\(num_i\),路径长度为\(d_i\).求这两个值简单\(dfs\)即可. 合并我们采用启发式合并以保证复杂度.首先,我们按当前根子树的最大深度升序将子树排列. 当我们处理到当