洛谷 P4178 Tree

洛谷 P4178 Tree

洛谷传送门

题目描述

给定一棵 nn 个节点的树,每条边有边权,求出树上两点距离小于等于 kk 的点对数量。

输入格式

第一行输入一个整数 nn,表示节点个数。

第二行到第 nn 行每行输入三个整数 u,v,wu,v,w ,表示 uu 与 vv 有一条边,边权是 ww

第 n+1n+1 行一个整数 kk

输出格式

一行一个整数,表示答案。


题解:

与POJ的那道题是一样的,就是少了个多组数据,还是洛谷好(逃。

还是点分治入门题,关于点分治:

详解点分治

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=4*1e4+10;
int n,k,ans;
int tot,head[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1];
bool v[maxn];
int size[maxn],mp[maxn],dist[maxn];
int sum,root,s;
void add(int x,int y,int z)
{
	to[++tot]=y;
	val[tot]=z;
	nxt[tot]=head[x];
	head[x]=tot;
}
void getroot(int x,int f)
{
	size[x]=1,mp[x]=0;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==f||v[y])
			continue;
		getroot(y,x);
		size[x]+=size[y];
		mp[x]=max(mp[x],size[y]);
	}
	mp[x]=max(mp[x],sum-size[x]);
	if(mp[x]<mp[root])
		root=x;
}
void getdis(int x,int f,int d)
{
	dist[++s]=d;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(v[y]||y==f)
			continue;
		getdis(y,x,d+val[i]);
	}
}
int calc(int x,int len)
{
	s=0;
	memset(dist,0,sizeof(dist));
	getdis(x,0,len);
	sort(dist+1,dist+s+1);
	int l=1,r=s,cnt=0;
	while(l<=r)
	{
		if(dist[r]+dist[l]<=k)
			cnt+=(r-l),l++;
		else
			r--;
	}
	return cnt;
}
void dfz(int x)
{
	ans+=calc(x,0);
	v[x]=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int y=to[i];
		if(v[y])
			continue;
		ans-=calc(y,val[i]);
		sum=size[y],root=0;
		getroot(y,0);
		dfz(root);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	scanf("%d",&k);
	mp[0]=sum=n;
	getroot(1,0);
	dfz(root);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13822204.html