/* 树的分治 题意:求树上两点间的距离<=k的点对数目; 因为n<=10000,暴搜一定会超时,所以很明显用的是树的点分治进行处理:点分治即为把树进行递归,分别对每个子树进行操作, 然后把每个子树的情况综合起来,对于这道题目,首先找到树根(即树的重心),对于该树,统计dis[i]+dis[j]<=k的数量, 将无根树转化成有根树进行观察。满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中。 如果我们已经知道了此时所有点到根的距离a[i],a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想, 分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中, 那么应该将其减掉(显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算,避免重复)。 在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心,这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)。 树的重心的算法可以线性求解。 树的重心的算法可以用一个dfs求出,但是因为这题的n是不确定的,我就写了两个dfs来求 */ #include<stdio.h> #include<string.h> #include<stdlib.h> #define N 11000 #define inf 0x3fffffff struct node{ int u,v,w,next; }bian[N*4]; int head[N],yong,num[N],ma,minn,m,nn,vis[N]; void init() { yong=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int w) { bian[yong].u=u; bian[yong].v=v; bian[yong].w=w; bian[yong].next=head[u]; head[u]=yong++; } int Max(int v,int vv) { return v>vv?v:vv; } void dfs4(int u,int fa) {//求出n int i; nn++; for(i=head[u];i!=-1;i=bian[i].next) { int v=bian[i].v; if(v!=fa&&!vis[v]) dfs4(v,u); } return ; } void dfs1(int u,int fa) {//求重心 num[u]=1; int i,tit=0; for(i=head[u];i!=-1;i=bian[i].next) { int v=bian[i].v; if(v!=fa&&!vis[v]) { dfs1(v,u); num[u]+=num[v]; tit=Max(tit,num[v]); } } tit=Max(tit,nn-num[u]); if(tit<minn) { minn=tit; ma=u; } return ; } int diss[N],len; void dfs3(int u,int fa,int w) {//统计重心到每个点的距离 int i; // printf("sa%d %d",u,w); diss[len++]=w; for(i=head[u];i!=-1;i=bian[i].next) { int v=bian[i].v; if(v!=fa&&!vis[v])//这里注意判断条件要加上!vis[v]; dfs3(v,u,w+bian[i].w); } return ; } int cmp(const void *a,const void *b) { return *(int *)a-*(int *)b; } int dfs2(int u,int fa,int w) {//求出重心为u时的符合条件的个数 len=0; dfs3(u,fa,w); qsort(diss,len,sizeof(diss[0]),cmp); int i,j,ans=0; // for(i=0;i<len;i++) // printf("%d ",diss[i]); // printf(" "); for(i=0,j=len-1;i<j;i++) { while(i<j&&diss[i]+diss[j]>m) j--; ans+=j-i; } return ans; } int dfs(int u) { minn=inf; memset(num,0,sizeof(num)); nn=0; dfs4(u,-1);//求n dfs1(u,-1);//求重心 // printf("u=%d cou=%d ",u,nn); // printf("%d ",ma); vis[ma]=1;//将重心标记走过 int ans=dfs2(ma,-1,0);//以ma为重心的符合条件的对数 //printf("a%d ",ans); int i; for(i=head[ma];i!=-1;i=bian[i].next) {//这里注意从ma开始遍历子树 int v=bian[i].v; if(!vis[v]) { ans-=dfs2(v,-1,bian[i].w);//将在一个子树上的情况减去,因为接下来会再算一遍,避免重复 ans+=dfs(v);//计算子树,递归求和 } } return ans; } int main() { int i,j,k,ii,n; while(scanf("%d%d",&n,&m),n||m) { init(); for(ii=1;ii<n;ii++) { scanf("%d%d%d",&i,&j,&k); addedge(i,j,k); addedge(j,i,k); } memset(vis,0,sizeof(vis));// k=dfs(1); printf("%d ",k); } return 0;}