链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1468
题解
点分治裸题。
说下过程,就是你每次找重心然后以重心为根进行建树,算出所有点到根的
dist
,扔到一个表里,两个指针扫一遍得出结果。然后分治下去,在找重心之前应该先
dfs
一遍把当前子树里的
dist
扔进表里,扫一遍把结果在
ans
中减去。
重心就是到所有点的距离之和最小的点。
代码
//点分治
#include <cstdio>
#include <algorithm>
#define maxn 40010
#define forp for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])
using namespace std;
int N, K, head[maxn], to[maxn<<1], nex[maxn<<1], w[maxn<<1], dist[maxn], G, list[maxn],
tot, size[maxn], sumG, ans;
bool grey[maxn];
inline void adde(int a, int b, int v)
{to[++tot]=b;w[tot]=v;nex[tot]=head[a];head[a]=tot;}
inline int calc()
{
int cnt=0, l=1, r=*list;
sort(list+1,list+*list+1);
while(l<r)
{
while(l<r and list[l]+list[r]>K)r--;
while(l<r and list[l]+list[r]<=K)cnt+=r-l, l++;
}
return cnt;
}
void dfs_jian(int pos, int pre)
{
list[++*list]=dist[pos];
for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])dfs_jian(to[p],pos);
}
void calc_dist(int pos, int pre)
{
list[++*list]=dist[pos];
forp dist[to[p]]=dist[pos]+w[p], calc_dist(to[p],pos);
}
int calc_size(int pos, int pre)
{
size[pos]=1;
forp size[pos]+=calc_size(to[p],pos);
return size[pos];
}
void findG(int pos, int pre, int sum)
{
if(sum<sumG)G=pos, sumG=sum;
int x=1;
forp x+=size[to[p]];
forp findG(to[p],pos,sum+(x-size[to[p]])*w[p]-size[to[p]]*w[p]);
}
void solve(int pos)
{
int x, i;
if(G)*list=0, dfs_jian(pos,-1), ans-=calc();
*list=0, calc_dist(pos,-1);
calc_size(pos,-1);
for(i=1,x=0;i<=*list;i++)x+=list[i];
G=pos, findG(pos,-1,sumG=x);
grey[G]=1, dist[G]=0;
*list=0, calc_dist(G,-1);
ans+=calc();
for(int p=head[G];p;p=nex[p])if(!grey[to[p]])solve(to[p]);
}
void init()
{
int a, b, v, i;
scanf("%d",&N);
for(i=1;i<N;i++)scanf("%d%d%d",&a,&b,&v), adde(a,b,v), adde(b,a,v);
scanf("%d",&K);
}
int main()
{
init();
solve(1);
printf("%d",ans);
return 0;
}