POJ1741 Tree

前言

今天刚刚学了点分治,赶紧把思路写一下,以防止以后忘记

分析

对于一个点,经过它的路径有两种可能,一是在一棵子树里(下图1-2-4),二是在两个子树里(下图2-1-5)。

那么对于情况二,我们可以把它拆成两条链相加。
对于情况一好像不行,但如果去分类讨论就会让程序更加复杂,但是发现它在它的子树里处理这种情况时就成了情况二,所以可以暂时不用管它,在递归下一层的时候返回来处理就行,不过需要加一个vis数组,防止情况搜多,这样就能够处理这种情况了。
但是有一种路径需要被减去,即合并(1-2-3和1-2-4)这种路径,因为显然不能合并啊,所以要减去。
减的方法是在往下递归的时候,以连向父节点的那条边的权值为起始值处理一遍答案,这种就相当于,模拟了一下从1搜下来的时候的情况,但是只搜这一棵树,就能够求出曾经多算的情况。
还有为了防止不会搜太多层,需要从重心开始搜。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int lqs=1e4+10;
struct Edge{
	int to,nxt,val;
}e[lqs<<1];
int h[lqs],idx;
void Ins(int a,int b,int c){
	e[++idx].to=b;e[idx].val=c;
	e[idx].nxt=h[a];h[a]=idx;
}
int n,k,rt,T,siz[lqs],w[lqs];
bool vis[lqs];
void dfs1(int u,int fa){
	siz[u]=1;w[u]=0;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(vis[v]||v==fa)continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		w[u]=max(w[u],siz[v]);
	}
	w[u]=max(w[u],T-siz[u]);
	if(w[u]<w[rt])rt=u;
}
int a[lqs],hh;
void dfs2(int u,int fa,int d){
	a[++hh]=d;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(vis[v]||v==fa)continue;
		dfs2(v,u,d+e[i].val);
	}
}
int ans;
int calc(int u,int d){
	hh=0;
	int sum=0;
	dfs2(u,0,d);
	sort(a+1,a+hh+1);
	for(int i=1,j=hh;;i++){
		while(j&&a[i]+a[j]>k)j--;
		if(i>j)break;
		sum+=j-i+1;
	}
	return sum;
}
void solve(int u){
	ans+=calc(u,0);
	vis[u]=1;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(vis[v])continue;
		ans-=calc(v,e[i].val);
		T=siz[v];rt=0;
		dfs1(v,0);
		solve(rt);
	}
}
void init(){
	memset(h,0,sizeof(h));
	ans=rt=idx=0;
	memset(vis,0,sizeof(vis));
}
int main(){
	w[0]=0x7f7f7f7f;
	while(~scanf("%d%d",&n,&k)&&n&&k){
		init();
		for(int i=1;i<n;i++){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			Ins(a,b,c);Ins(b,a,c);
		}
		T=n;
		dfs1(1,0);
		solve(rt);
		printf("%d\n",ans-n);
	}
}
posted @ 2020-05-30 12:51  An_Fly  阅读(110)  评论(1编辑  收藏  举报