分治就是分而治之。额……好像这话已经被说烂了。
学树分治之前我们先来回顾下线性分治。一般的线性分治都是将大的区间分成多个小的区间(一般是两个),然后将已处理好的小区间合并从而得到了这个大区间的结果,为了使分的次数尽可能少,应每次使分的小区间的大小尽可能平均,这样分的次数就会是log级别的。
好,现在我们要分树,参考线性分治的方法,每次选一个点,使删除这个点后被分开的连通块大小尽可能平均,暂且把这个点称为“中心点”。
等等我们要先了解树分治是解决哪类问题的。个人认为:树分治一般用来处理树上的“全部路径”问题。故名思意“全部路径”就是树上所有点对之间的路径,看似这样的路径毫无规律,其实对于树上某一点来说,我们可以的将这些路径分为:通过这点的和永不通过这点的,其实不通过这点的路径一定通过其他某一个点。所以我们只需要求通过一个点的所有路径,求完后将这点彻底删除,然后同样的方法求其他点,直到遍历完所有的点为止。(时机已成熟,看来是时候该引入点分了)还记得上面的“中心点”吧,我们如果一个一个点没有一定顺序的去处理虽然可以得到正确答案,但是太慢了,如果每次递归处理“中心点”复杂度将减少很多很多,因为这样每次处理一个“中心点”的时,连通块的大小都是次数级减小的(多画几颗树体会一下就知道了)。
总结一下树上点分治的步骤:
1:找中心点
2:处理该中心点所在的连通块中的通过该中心点的路径的答案(比较拗口,仔细琢磨下)
3:删除该中心点,递归处理与该点相邻的连通块
具体实现看代码吧,入门题
AC代码(老哥加油哦):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10005;
struct Edge
{
int a,b,len,pre;
}edge[N*2];
int vis[N],pre[N],siz[N],aa[N];
int sum,root,root_siz,edge_num,num,k,ans;
char ch;
inline int read()///读入挂
{
int res=0;
while(ch=getchar(),ch>'9'||ch<'0');
res=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')res=10*res+ch-'0';
return res;
}
inline void addedge(int a,int b,int len)///加边
{
edge[++edge_num]=(Edge){a,b,len,pre[a]};
pre[a]=edge_num;
}
void dfssum(int u,int f)///求连通块大小
{
sum++;
for(int i=pre[u];i;i=edge[i].pre)
{
int v=edge[i].b;
if(vis[v]||v==f)continue;
dfssum(v,u);
}
}
void dfsroot(int u,int f)///求中心点
{
siz[u]=1;
int mx=0;
for(int i=pre[u];i;i=edge[i].pre)
{
int v=edge[i].b;
if(vis[v]||v==f)continue;
dfsroot(v,u);
siz[u]+=siz[v];
mx=max(siz[v],mx);
}
mx=max(mx,sum-siz[u]);
if(mx<root_siz)root=u,root_siz=mx;
}
void dfs(int u,int f,int d)///求该连通块中所有点到这点的距离
{
aa[++num]=d;
for(int i=pre[u];i;i=edge[i].pre)
{
int v=edge[i].b;
if(vis[v]||v==f)continue;
dfs(v,u,d+edge[i].len);
}
}
int jisuan(int u,int add)///算该连通块中满足条件或不满足条件的路径数量
{
num=0;
dfs(u,0,add);
sort(aa+1,aa+1+num);
int ans_num=0,l=1,r=num;
while(l<r)
{
while(l<r&&aa[l]+aa[r]>k)r--;
if(l==r)break;
ans_num+=r-l;
l++;
}
return ans_num;
}
void chuli(int u)///将n个点按中心点顺序依次求对答案的贡献值
{
sum=0;
dfssum(u,0);
root_siz=2e9;
dfsroot(u,0);
ans+=jisuan(root,0);
///printf("#%d %d %d %d\n",root,u,sum,root_siz);
vis[root]=1;
for(int i=pre[root];i;i=edge[i].pre)
{
int v=edge[i].b;
if(vis[v])continue;
ans-=jisuan(v,edge[i].len);
chuli(v);
}
}
int main()
{
int n;
while(1)
{
n=read(),k=read();
if(n+k==0)break;
for(int i=1;i<=n;i++)
pre[i]=0,vis[i]=0;
int a,b,len;
edge_num=0;
for(int i=1;i<n;i++)
{
a=read(),b=read(),len=read();
addedge(a,b,len);
addedge(b,a,len);
}
ans=0;
chuli(1);
printf("%d\n",ans);
}
return 0;
}