題意:求一顆節點數爲n(n<=10000)的樹中,距離不超過k的點對(x,y)(x<y)的數量。
解:考慮點分治
分情況討論,過根節點的鏈直接dfs+掃一遍統計方案數,方案數計算方法爲:將該樹所有離根節點的距離存下來排序,然後統計個數,再減去在同一子樹內的點對數。
不過根節點的鏈通過遞歸子樹求出,重複上面步驟。
爲了防止子樹退化成鏈從而退化爲O(n^2),所以我們對於每一顆子樹求出其重心再進行計算。
(重心是使該樹最大子樹最小的節點)
代碼(204ms):
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<vector>
#define uint unsigned int
#define For(i,j,k) for(register uint i=(j);i<=(uint)k;i++)
#define Forr(i,j,k) for(register uint i=(j);i>=(uint)k;i--)
#define Rep(i,u) for(register uint i=Begin[u],v=to[i];i;i=Next[i],v=to[i])
#define Set(a,b) memset((a),b,sizeof(a))
#define ull unsigned long long
#define ll long long
using namespace std;
const int N=10010,M=20010;
int Begin[N],Next[M],to[M],e=1,w[M],p[M],siz[N],dis[N],fa[N];
int n,k,ans;
inline void read(int &x){
x=0;char c=getchar();int f=0;
while(c<'0' || c>'9'){c=getchar();if (c=='-')f=1;}
while(c>='0' && c<='9')x=x*10+c-'0',c=getchar();if (f)x=-x;
}
inline void add(int x,int y,int z){
to[++e]=y,Next[e]=Begin[x],Begin[x]=e,w[e]=z,p[e]=0;
}
inline void init(){
Set(Begin,0),e=1,ans=0;
}
void Getcg(int u,int sz,int &cg){
uint flag(1);
siz[u]=1;
Rep(i,u)
if(!p[i]&&fa[u]!=v){
fa[v]=u,
Getcg(v,sz,cg),siz[u]+=siz[v];
if (siz[v]>sz>>1)flag=0;
}
if(sz-siz[u]>sz>>1)flag=0;
if(flag)cg=u;
}//找出該子樹的重心
void dfs1(int u,int d){
dis[u]=d;
Rep(i,u)
if (!p[i]&&fa[u]!=v)
fa[v]=u,dfs1(v,(dis[v]=d+w[i]));
}//求出每個節點到根節點的距離和每個節點的父親
int top,ret,s[N];
void dfs2(int u){
s[++top]=dis[u];
Rep(i,u)
if (!p[i]&&fa[u]!=v)
dfs2(v);
}//將所有點離根節點的距離入棧
int calc(int u){
top=ret=0;
dfs2(u);
sort(s+1,s+top+1);
for(int i=1,j=top;i<=top;i++){
for(;j&&s[i]+s[j]>k;j--);
ret+=j;
}
return ret;
}//計算距離小於k的點對個數
void Tree_Divide(int rt,int sz){
int cg;
fa[rt]=0;
Getcg(rt,sz,cg);
siz[fa[cg]]=sz-siz[cg];
fa[cg]=0;
dfs1(cg,0);
ans+=calc(cg);
Rep(i,cg)
if (!p[i])ans-=calc(v);
Rep(i,cg)
if (!p[i])p[i]=p[i^1]=1,Tree_Divide(v,siz[v]);
}//點分治求解
int main(){
while(scanf("%d%d",&n,&k)&&(n||k)){
int x,y,z;
init();
For(i,1,n-1)
read(x),read(y),read(z),add(x,y,z),add(y,x,z);
Tree_Divide(1,n);
printf("%d\n",(ans-n)/2);
}
return 0;
}