[POJ1741]Tree(点分治)

树分治之点分治入门

所谓点分治,就是对于树针对点的分治处理

首先找出重心以保证时间复杂度

然后递归处理所有子树

 

对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况

  1. 路径过当前根
  2. 路径在子树中(递归处理)

那么关键就是如何计算第一种情况

设d[i]表示点i到当前根rt的距离,可以将d数组排序后线性复杂度求

然而此时会有些点对是在同一棵子树中,这些情况要减去

注意每次递归都要找一次重心以保证效率

这样复杂度就是O(nlog2n)

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define Inf 0x7fffffff
#define N 10010
using namespace std;

struct info{int to,nex,w;}e[N*2];
int n,k,tot,head[N],d[N],rt,Ans,sum,f[N],sz[N],dep[N];
bool vis[N];

void Link(int u,int v,int w){
	e[++tot].nex=head[u];head[u]=tot;e[tot].to=v;e[tot].w=w;
}

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void getrt(int u,int fa){
	sz[u]=1,f[u]=0;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(v==fa||vis[v]) continue;
		getrt(v,u);
		sz[u]+=sz[v];
		f[u]=max(f[u],sz[v]);
	}
	f[u]=max(f[u],sum-sz[u]);
	if(f[rt]>f[u]) rt=u;
}

void Init(){
	tot=0,rt=0,Ans=0; 
	memset(head,0,sizeof(head));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		Link(u,v,w),Link(v,u,w);
	}
	sum=n,f[0]=Inf;
	getrt(1,0);
}

void getdep(int u,int fa){
	dep[++dep[0]]=d[u];
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(v==fa||vis[v]) continue;
		d[v]=d[u]+e[i].w;
		getdep(v,u);
	}
}

int cal(int u,int cur){
	d[u]=cur,dep[0]=0;
	getdep(u,0);
	sort(dep+1,dep+dep[0]+1);
	int t=0;
	for(int l=1,r=dep[0];l<r;)
		if(dep[l]+dep[r]<=k) t+=r-l,++l;
		else --r;
	return t;
}

void solve(int u){
	Ans+=cal(u,0);
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(vis[v]) continue;
		Ans-=cal(v,e[i].w);
		sum=sz[v];
		getrt(v,rt=0);
		solve(rt);
	}
} 

int main(){
	while(~scanf("%d%d",&n,&k)&&n){
		Init();
		solve(rt);
		printf("%d\n",Ans);
	}
}

 

posted @ 2018-05-28 20:22  void_f  阅读(201)  评论(0编辑  收藏  举报