和那个poj1741是同一道题目,不过既然写了就顺便说说吧。
寻找重心,保证复杂度。
然后对于每个重心(从上往下),计算路径经过他的和,减去路径起点终点都在子树内的路径。
如何计算?扫一遍这个点的所有子树,记录一下路径和,存在一个数组里面,然后双指针扫扫就没了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
const int inf=1e9;
int f[N];
int n,k,ans,cnt,tot,sum,root;
int head[N],go[N],val[N],next[N];
int son[N],dep[N],d[N];
bool vis[N];
inline void add(int x,int y,int z)
{
go[++tot]=y;
val[tot]=z;
next[tot]=head[x];
head[x]=tot;
}
int tim;
inline void getroot(int x,int fa)
{
son[x]=1;
f[x]=0;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (vis[v]||v==fa)continue;
getroot(v,x);
son[x]+=son[v];
f[x]=max(f[x],son[v]);
}
f[x]=max(f[x],sum-son[x]);
if (f[x]<f[root])root=x;
}
inline void getdep(int x,int fa)
{
dep[++dep[0]]=d[x];
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (v==fa||vis[v])continue;
d[v]=d[x]+val[i];
getdep(v,x);
}
}
inline int cal(int x,int now)
{
d[x]=now,dep[0]=0;
getdep(x,0);
sort(dep+1,dep+dep[0]+1);
int t=0,l,r;
l=1,r=dep[0];
while (l<r)
{
if (dep[l]+dep[r]<=k)
{
t+=r-l;
l++;
}
else r--;
}
return t;
}
inline void work(int x)
{
ans+=cal(x,0);
vis[x]=1;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (vis[v])continue;
ans-=cal(v,val[i]);
sum=son[v];
root=0;
getroot(v,root);
work(root);
}
}
int main()
{
scanf("%d",&n);
fo(i,1,n-1)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
scanf("%d",&k);
sum=n;
f[0]=inf;
getroot(1,0);
work(root);
printf("%d\n",ans);
}