洛谷 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;
}