点分治 模板
1.选取一个点,把无根树变成有根树。通过树形dp的方式选择。用son记录点的子树大小,用F算出最大的子树,当F[x]<F[root]时,更换root.
2.再次通过dfs处理树上的deep和距离。
3.处理连通块中通过根节点的路径。减去同一子树内部的路径。
4.递归。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
using namespace std;
typedef long long ll;
const int maxn = 10000+5;
const int maxm = 20000+10;
bool vis[maxn];
int head[maxn];
int nxt[maxm],w[maxm],e[maxm],n,k,root,sum,x,y,z;
int ans;
int F[maxn];//sum of son
int son[maxn];
int dep[maxn];
int d[maxn];
void getroot(int x,int fa){
son[x]=1;F[x]=0;
for(int k=head[x];k!=-1;k=nxt[k]){
if(e[k]==fa||vis[e[k]]) continue;
getroot(e[k],x);
son[x]+=son[e[k]];
F[x]=max(F[x],son[x]);
}
F[x]=max(F[x],sum-F[x]);
if(F[x]<F[root]) root=x;
}
void getdep(int x,int fa){
dep[++dep[0]]=d[x];
for(int k=head[x];k!=-1;k=nxt[k]){
if(e[k]==fa||vis[e[k]]) continue;
d[e[k]]=d[x]+w[k];
getdep(e[k],x);
}
}
int calc(int x,int v){
d[x]=v;dep[0]=0;
getdep(x,0);
sort(dep+1,dep+1+dep[0]);
int l=1,r=dep[0],ret=0;
while(l<r){
if(dep[r]+dep[l]<=k){
ret+=r-l;
l++;
} else {
r--;
}
}
return ret;
}
void solve(int x){
ans+=calc(x,0);
vis[x]=1;
for(int k=head[x];k!=-1;k=nxt[k]){
if(vis[e[k]]) continue;
ans-=calc(e[k],w[k]);
sum=son[e[k]];
root=0;
getroot(e[k],0);
solve(root);
}
}
int main(){
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&k)){
if(!n&&!k) break;
ans=root=0;
memset(vis,0,sizeof vis);
memset(head,-1,sizeof head);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
e[i]=y;w[i]=z;nxt[i]=head[x];head[x]=i;
e[i+n]=x;w[i+n]=z;nxt[i+n]=head[y];head[y]=i+n;
}
F[0]=INT_MAX;sum=n;
getroot(1,0);
solve(root);
printf("%d\n",ans);
}
return 0;
}