/* 树基于边的分治算法,计算树中距离小于等于k的点对数目 点的下标从1开始,树里实际的节点个数的上界是N*3 当点只有一个的时候,得到的是空树(这个需要注意), 分治树有O(log(NUM))层,如果会动态改变树里的信息的话, 还需要记录每个的节点在每一层是在左子树还是右子树,以及在这一层 到根的距离信息(这里的根指的是这一层的分治边的两个点中到该点距离 较近的那个点)。 树重构后[1,n]为原树中的 节点,[n+1,cnt]为虚拟节点 */ #include <cstdio> #include <algorithm> #include <cmath> #include <ctime> using namespace std; const int N = 10005; const int NUM=N*3; //重构后树中的节点的个数最大值 typedef int wtype; //边权的类型 const wtype INF = 0X7FFFFFFF; struct e{ int u, v; wtype len; e *nxt, *pair; bool used; //标记一条边是否已经被选过了 //分治树的数据结构 e *ls, *rs; }*fir[NUM], es[(NUM+N)*2]; int n, en, cnt, k; //cnt为重构后的树中的节点 int que[NUM], par[NUM], size[NUM]; e* eque[NUM]; wtype len[NUM]; wtype lens[2][NUM]; int nums[2], ans; void clear(int n){ en = 0; int i; for(i = 1; i <= n; i++){ fir[i] = NULL; } } void add_e(int u, int v, e* pair, wtype len){ es[en].u = u; es[en].v = v; es[en].pair = pair; es[en].len = len; es[en].used = false; es[en].nxt = fir[u]; fir[u] = &es[en++]; } void insert(int u, int v, wtype len){ add_e(u, v, &es[en+1], len); add_e(v, u, &es[en-1], len); } void adjust(int l, int r, int f, int& cnt){ if(r-l <= 1){ while(l <= r){ int u = que[l]; par[u] = f; insert(u, f, eque[l++]->len); } }else{ int mid = (l+r)>>1, ls, rs; fir[ls=++cnt] = NULL; insert(ls, f, 0); fir[rs=++cnt] = NULL; insert(rs, f, 0); par[ls]=par[rs]=f; adjust(l, mid, ls, cnt); adjust(mid+1, r, rs, cnt); } } void adjust(int n){ //重构树 int l, r, u, v, mid; e* cur; cnt = n; for(l=r=0, par[1]=-1, que[r++]=1; l!=r; ){ for(cur = fir[u=que[l++]], mid=r; cur; cur = cur->nxt){ if(!cur->used && (v=cur->v) != par[u]){ que[r] = v; par[v] = u; eque[r++] = cur; } } if(r-mid > 2){ adjust(mid, r-1, u, cnt); //删边,只需把那些边的used标记为true即可 while(mid<r){ eque[mid]->used=true; eque[mid++]->pair->used=true; } } } } inline int ABS(int a){ return a>0 ? a:-a; } e* getRoot(int s, bool isLeft){ //找根节点 e *ans=NULL, *cur; int l, r, u, num, v, ds, i; nums[isLeft]=0; for(l=r=0, par[s]=-1, len[s]=0, que[r++]=s; l!=r; ){ for(cur=fir[u=que[l++]]; cur; cur=cur->nxt){ if(!cur->used && (v=cur->v) != par[u]){ eque[r]=cur; que[r++]=v; par[v]=u; len[v]=len[u]+cur->len; } } } for(i=r-1; i>=0; i--){ if(que[i] <= n){ lens[isLeft][nums[isLeft]++]=len[que[i]]; } } for(num=r, i=r-1; i>=1; i--){//队列里的第一个点不可能被选择 u=que[i]; size[u]=1; for(cur=fir[u]; cur; cur=cur->nxt){ if(!cur->used && (v=cur->v) != par[u]){ size[u] += size[v]; } } if(ans==NULL || ds > ABS(num-size[u]*2)){ ans=eque[i]; ds=ABS(num-size[u]*2); } } return ans; } void division(e*& r, int d){ if(!r) return ; //r->u作为左孩子,r->v作为右孩子 r->used = true; r->pair->used = true; r->ls = getRoot(r->u, true); r->rs = getRoot(r->v, false); int il, ir; //计算点对 sort(lens[0], lens[0]+nums[0]); sort(lens[1], lens[1]+nums[1]); for(il=0, ir=nums[0]-1; il<nums[1]; il++){ while(ir >= 0 && lens[1][il]+lens[0][ir]+r->len>k) ir--; ans += ir+1; } division(r->ls, d+1); division(r->rs, d+1); } //r为根节点(初始为空,s为一个起点,n为点的个数) void build(e*& r, int s, int n){ //构建分治树 adjust(n); //首先得调整树的结构 r = getRoot(s, false); division(r, 1); } //输入一个整数 template<typename T> void getSNum(T& ans){ char ch; int s; while(true){ ch = getchar(); if((ch >= '0' && ch <= '9') || ch == '-') break; } if(ch == '-'){ s = -1; ans = 0; }else{ s = 1; ans = ch -'0'; } while(true){ ch = getchar(); if(!(ch >= '0' && ch <= '9')) break; ans = ans*10+ch-'0'; } ans *= s; } bool input(){ scanf("%d%d", &n, &k); if(n == 0 && k == 0) return false; int u, v, i; wtype len; clear(n); for(i = 1; i < n; i++){ getSNum(u); getSNum(v); getSNum(len); insert(u, v, len); } return true; } void solve(){ e* root=NULL; ans=0; if(n > 1){ build(root, 1, n); } printf("%d\n", ans); } int main(){ //freopen("in.txt", "r", stdin); while(input()) solve(); return 0; }