【学术篇】SPOJ FTOUR2 点分治

淀粉质入门第一道 (现在个人认为spoj比bzoj要好_(:з」∠)_
关于点分治的话推荐去看一看漆子超的论文>>>这里这里<<<

之前一直试图入点分治坑, 但是因为种(bu)种(duan)原(tui)因(fei)也没有入...
结果经常碰到点分治的题目... 然后就各种弃疗...
不少点分治的题目有非常明显的特征... 通常是给一棵树, 然后问你满足xx条件的路径有多少条/是否存在/最大(小)权值之类的...
然后点分治的做法也不尽相同 大致能写出如下的伪代码(好吧还是用python高亮)

def solve(x):        # 处理以x为根的子树
    vis[x]=1           # 把x标记为操作过(视为删掉)
    findroot(x)       # 找到以x为根的子树的重心
    calc(x)             # 统计过x的路径的答案
    for i in son[x]:  # 对于每个儿子
        solve(i)        # 递归处理答案

然后刚入门第一道就不能算是很裸的点分治_(:з」∠)_
几乎抄了黄学长的代码, 在此表示感谢..
<题目の传送门>
hzwer题解の传送门
题目大意:
给一棵树, 有\(m\)个点上有一个标记, 边有边权, 可正可负.
询问路径上带标记的点不超过\(k\)的路径的最大权值是多少.

首先朴素的思路就是暴力嘛... 复杂度\(O(n^2)\)的.. 可能能拿30~40(但这是spoj所以并没有什么部分分..)
我们要考虑复杂度更好的做法.. 首先根据上面我们说过的特征, 可以看出这题应该可以用点分治做...
因为后面是递归处理的, 我们只需要考虑如何统计合法的过根节点的路径的答案就行了..


我们需要处理出\(dep[y]\)\(dis[y]\)两个数组, 分别表示\(y\)到当前的根节点(以下的根节点均指当前子树的根节点, 因为原来的根节点处理过就删掉了)的路径上的带标记节点个数和路径权值和. 这个可以通过一遍dfs\(O(n)\)完成...
然后我们考虑有哪些路径会对答案产生影响...
假如我们要处理\(x\)\(i\)个子树, 那么前\(i-1\)个子树中的点可以与这个子树中的点确定一条路径.
我们再用一遍\(O(n)\)的dfs处理出\(mx[t]\)这个数组, 表示从前\(i-1\)棵子树中到根节点的经过\(t\)个带标记节点的路径的最大长度..
那我们就可以得到:

\[ans=max\{mx[t]+dis[x]\} (dep[x]+t<=k) \]

但是这个dep[x]是会变的, 所以我们可以把儿子按照dep排序一波再做, 就可以顺着推过去了, 据说这样的复杂度是\(O(n)\)级别的..
然后排序的话总共也就只是\(O(nlogn)\)级别的东西, 配合着点分治的复杂度, 最后就是\(O(nlogn)\)咯..

代码:

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define depth	first
#define id		second
const int N=202020;
inline int gn(int a=0,char c=0,int f=1){
	for(;(c<48||c>57)&&c!='-';c=getchar());if(c=='-')c=getchar(),f=-1;
	for(;c>47&&c<58;c=getchar()) a=a*10+c-'0'; return a*f;
}
struct edge{
	int to,next,data;
}e[N<<1]; int v[N],tot,n,m,k,rt,size,ans;
void buildedge(int x,int y,int z){
	e[++tot].to=y; e[tot].next=v[x]; v[x]=tot; e[tot].data=z;
	e[++tot].to=x; e[tot].next=v[y]; v[y]=tot; e[tot].data=z;
}
int q[N],fa[N],son[N],sz[N],mx[N],tmp[N],dep[N],d[N],depmx;
bool vis[N],a[N];
void findrt(int x,int fa){
	sz[x]=1; son[x]=0;
	for(int i=v[x];i;i=e[i].next)
		if(!vis[e[i].to]&&e[i].to!=fa){
			findrt(e[i].to,x);
			son[x]=max(son[x],sz[e[i].to]);
			sz[x]+=sz[e[i].to];
		}
	son[x]=max(son[x],size-sz[x]);
	if(son[x]<son[rt]) rt=x;
}
void calcdis(int x,int fa){
	depmx=max(depmx,dep[x]);
	for(int i=v[x];i;i=e[i].next)
		if(!vis[e[i].to]&&e[i].to!=fa){
			dep[e[i].to]=dep[x]+a[e[i].to];
			d[e[i].to]=d[x]+e[i].data;
			calcdis(e[i].to,x);
		}
}
void calcmax(int x,int fa){
	tmp[dep[x]]=max(tmp[dep[x]],d[x]);
	for(int i=v[x];i;i=e[i].next)
		if(!vis[e[i].to]&&e[i].to!=fa)
			calcmax(e[i].to,x);
}
vector<pair<int,int> > vec;
void solve(int x){
	vis[x]=1; vec.clear(); if(a[x]) --k;
	for(int i=v[x];i;i=e[i].next)
		if(!vis[e[i].to]){
			depmx=0;
			dep[e[i].to]=a[e[i].to];
			d[e[i].to]=e[i].data;
			calcdis(e[i].to,x);
			vec.push_back(make_pair(depmx,e[i].to));
		}
	sort(vec.begin(),vec.end());
	for(int i=0;i<vec.size();++i){
		calcmax(vec[i].id,x);
		int now=0;
		if(i!=0)
			for(int j=vec[i].depth;j>=0;--j){
				while(now+j<k&&now<vec[i-1].depth)
					++now,mx[now]=max(mx[now],mx[now-1]);
				if(now+j<=k) ans=max(ans,mx[now]+tmp[j]);
			}
		if(i!=vec.size()-1)
			for(int j=0;j<=vec[i].depth;++j)
				mx[j]=max(mx[j],tmp[j]),tmp[j]=0;
		else
			for(int j=0;j<=vec[i].depth;++j){
				if(j<=k) ans=max(ans,max(tmp[j],mx[j]));
				tmp[j]=mx[j]=0;
			}
	}
	if(a[x]) ++k;
	for(int i=v[x];i;i=e[i].next)
		if(!vis[e[i].to]){
			rt=0; size=sz[e[i].to];
			findrt(e[i].to,x);
			solve(rt);
		}
}
int main(){
	n=gn(),k=gn(),m=gn();
	for(int i=1;i<=m;++i) a[gn()]=1;
	for(int i=1;i<n;++i){
		int x=gn(),y=gn(),z=gn();
		buildedge(x,y,z);
	}
	size=son[0]=n; findrt(1,0);
	solve(rt);
	printf("%d",ans);
}
posted @ 2018-02-27 09:51  Enzymii  阅读(149)  评论(0编辑  收藏  举报