点分治
给定一棵带权树,求树上路径小于等于k,求简单路径条数,不可以有重边;
显然,对于每个点来说,有两种情况
1,过这个点的路径;
2,不过这个点的路径;
对于第二种情况,显然会经过其他节点,为了避免算重,每次算完之后把当前点删去;
为了使删完后的树尽量平衡,可以选择重心;
对于1这种情况,以选出来的重心为根节点,遍历一遍树;
算出其他点到root的距离d[i],从小到大排序,用双指针;
左端点枚举l,右端点不断向左移,每次答案加上r-l,因为l自己本身不算;
由于题目要求简单路径,所以如果其中有两个点在同一个子树中,不符合情况;
可以用容斥解决,可以单独算在每个子树的情况,用总答案减去即可;
对于第二种情况,遍历删去当前节点的每一个棵子树,重复1过程;
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=4e4+7;
const int inf=0x3f3f3f3f;
struct edge{
int v,w,nxt;
}e[N<<1];
int n,k,now_siz,sz,root,ans,cnt,tot;
int head[N],d[N],st[N],siz[N],vis[N];
void add_edge(int u,int v,int w){
e[++cnt]=(edge){v,w,head[u]};
head[u]=cnt;
}
void get_root(int u,int fa){
siz[u]=1;
int res=0;
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].v;
if(vis[to]||to==fa) continue;
get_root(to,u);
siz[u]+=siz[to];
res=max(res,siz[to]);
}
res=max(res,sz-siz[u]);
if(res<now_siz) root=u,now_siz=res;
}
void get_dis(int u,int fa){
st[++tot]=d[u];
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].v;
if(vis[to]||to==fa) continue;
d[to]=d[u]+e[i].w;
get_dis(to,u);
}
}
int solve(int u,int dis){
d[u]=dis;
tot=0;
get_dis(u,0);
sort(st+1,st+tot+1);
// for(int i = 1;i <= tot; i++) cout << st[i] << " ";
// cout <<endl;
int l=1,r=tot,res=0;
for(;l<r;l++){
while(st[r]+st[l]>k){
r--;
}
if(l<r) res+=r-l;
}
// cout<<res<<"
";
return res;
}
void dfs(int u){
vis[u]=1;
// cout<<"-->"<<u<<"
";
ans+=solve(u,0);
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].v;
if(vis[to]) continue;
ans-=solve(to,e[i].w);
now_siz=inf,sz=siz[to],root=0;
get_root(to,0);
dfs(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_edge(x,y,z);
add_edge(y,x,z);
}
scanf("%d",&k);
now_siz=inf,sz=n,root=0;
get_root(1,0);
dfs(root);
cout<<ans;
}