楼教主的题。
//每次选择树的重心,删去重心形成多棵子树(子树一定小于上一层的一半),最多log(n)层 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=22222; int ev[N],ew[N],nxt[N],head[N],e; int vis[N],dp[N],arr[N]; int n,k,core,sz,pmn,ed; void init() { memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); e=ed=0; } void add(int u,int v,int w) { ev[e]=v,ew[e]=w,nxt[e]=head[u];head[u]=e++; } void calsz(int u,int p) //辅助求重心 { for(int i=head[u];~i;i=nxt[i]) if(ev[i]!=p&&!vis[ev[i]]) calsz(ev[i],u),sz++; } void calcore(int u,int p) //求重心 { dp[u]=1;int mx=0,v; for(int i=head[u];~i;i=nxt[i]) if(ev[i]!=p&&!vis[v=ev[i]]) { calcore(v,u); dp[u]+=dp[v]; mx=max(mx,dp[v]); } mx=max(mx,sz-dp[u]); if(mx<pmn) pmn=mx,core=u; } int cntnum(int *arr,int len) //arr[0]-arr[len-1]满足条件的点对数 { int ans=0; sort(arr,arr+len); for(int i=0,j=len-1;i<j;i++) { while(i<j&&arr[i]+arr[j]>k) j--; ans+=j-i; } return ans; } void make(int u,int p,int len) //将子树所有结点到当前根的距离放入arr数组中 { arr[ed++]=len; for(int i=head[u];~i;i=nxt[i]) if(ev[i]!=p&&!vis[ev[i]]) make(ev[i],u,len+ew[i]); } int dfs(int u) { pmn=1e8; sz=1; calsz(u,u); calcore(u,u); ed=vis[u=core]=1; int ans=0; for(int i=head[u];~i;i=nxt[i]) { int st=ed; if(!vis[ev[i]]) make(ev[i],u,ew[i]); ans-=cntnum(arr+st,ed-st);//先将下一层子树内部满足条件的点对减掉 } ans+=cntnum(arr,ed); for(int i=head[u];~i;i=nxt[i]) if(!vis[ev[i]]) ans+=dfs(ev[i]);//统计当前子树点对 return ans; } int main() { while(scanf("%d%d",&n,&k),n||k) { init(); for(int i=1; i<n; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w),add(v,u,w); } printf("%d\n",dfs(1)); } return 0; }