点分治入门例题呀,本来打算这星期多搞几道点分治的题,可是有其心而力不足啊。
emm这星期做了四五道莫比乌斯反演,还有两套模拟赛,那点分治放在下星期找一天做吧。
这题题解先写好吧。
题意:给你一棵树,和树上每条边的长度,问有多少对点满足两点间的距离小于等于k。
稍微说一下点分治的过程。
1、找重心
2、处理询问
3、分治
此题里就是要处理从一个子树到另一个子树距离小于等于k。
那么可以先处理出根到子树每个点的深度,然后排序类似于两个指针做。
就可以轻松搞定啦~具体还看代码吧。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 10000+115
using namespace std;
int cnt,n,m,ALL,ans,root,x,y,z,tot,head[N],Next[N+N],len[N+N],vet[N+N],deep[N],d[N],size[N],f[N],vis[N];
const int inf=1e9;
inline void add_edge(int x,int y,int z){
vet[++cnt]=y;Next[cnt]=head[x];len[cnt]=z;head[x]=cnt;
}
void get_root(int x,int fa){
f[x]=0;size[x]=1;
for(int i=head[x];i;i=Next[i])
if(!vis[vet[i]] && vet[i] != fa){
get_root(vet[i],x);size[x]+=size[vet[i]];
f[x]=max(f[x],size[vet[i]]);
}
f[x]=max(f[x],ALL-size[x]);
if(f[root]>f[x]) root=x;
}
void get_deep(int x,int fa){
d[++tot]=deep[x];
for(int i=head[x];i;i=Next[i])
if(!vis[vet[i]] && vet[i] != fa) deep[vet[i]]=deep[x]+len[i],get_deep(vet[i],x);
}
inline int calc(int x){
tot=0;get_deep(x,-1);
sort(d+1,d+tot+1);
int i=1,j=tot,sum=0;
while(i<j){
if(d[i]+d[j]<=m) sum+=j-i,i++;
else j--;
}
return sum;
}
void dfs(int x){
deep[x]=0;vis[x]=1;
ans+=calc(x);
for(int i=head[x];i;i=Next[i])
if(!vis[vet[i]]){
deep[vet[i]]=len[i];
ans-=calc(vet[i]);
ALL=size[vet[i]];
root=0;
get_root(vet[i],-1);
dfs(root);
}
}
int main()
{
while(scanf("%d%d",&n,&m) && (n || m)){
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
cnt=0;ans=0;
for(int i=1;i<n;i++){scanf("%d%d%d",&x,&y,&z);add_edge(x,y,z);add_edge(y,x,z);}
f[0]=inf,ALL=n;
root=0;get_root(1,-1);
dfs(root);
printf("%d\n",ans);
}
return 0;
}