题目链接:http://poj.org/problem?id=1741
题意:给出一棵树,定义dis(u,v)为两个节点之间的距离。dis(u,v)<=K时成(u,v)是合法的。问有多少个合法的顶点对(u,v)?
思路:(1)树的重心:删掉树的一个顶点,树将分成若干个子树。若有一个顶点,删掉后,使得子树节点的最大值最小,则该点称为重心。
(2)树的点分治:不知道标准的定义。我的理解就是找到一个点,删掉,然后递归的处理各个子树。这就是树的点分治的思想。
(3)对于本题,每次找到当前树的重心作为根节点。首先我们计算出通过根节点的合法顶点对个数,然后递归处理子树即可。首先,求出每个点到根节点的距离。然后将所有距离排序,则可以利用单调性O(n)计算出合法的顶点对。如下:
sort(V1.begin(),V1.end());
int L=0,R=SZ(V1)-1;
while(L<R)
{
if(V1[L]+V1[R]<=m) ans+=R-L,L++;
else R--;
}
此时,有一些可能是只通过某个子树内的,因此还要先将其减去。
int n,m,ans;
vector<pair<int,int> > g[N];
int D[N],visit[N];
int s[N],Max[N];
vector<int> V1,V2;
int tot;
void DFS(int u,int pre)
{
s[u]=1; Max[u]=0; tot++;
int i,v;
FOR0(i,SZ(g[u]))
{
v=g[u][i].first;
if(v==pre||visit[v]) continue;
DFS(v,u);
s[u]+=s[v];
if(s[v]>Max[u]) Max[u]=s[v];
}
V1.pb(Max[u]); V2.pb(u);
}
int getRoot(int u)
{
V1.clear(); V2.clear(); tot=0;
DFS(u,-1);
int Min=INF,ans,i,temp;
FOR0(i,SZ(V1))
{
temp=max(V1[i],tot-V1[i]);
if(temp<Min) Min=temp,ans=V2[i];
}
return ans;
}
void getDeep(int u,int len,int pre)
{
V1.pb(len);
int i,v,w;
FOR0(i,SZ(g[u]))
{
v=g[u][i].first;
w=g[u][i].second;
if(v==pre||visit[v]) continue;
getDeep(v,len+w,u);
}
}
void solve(int u)
{
int root=getRoot(u);
V1.clear();
getDeep(root,0,-1);
sort(V1.begin(),V1.end());
int L=0,R=SZ(V1)-1;
while(L<R)
{
if(V1[L]+V1[R]<=m) ans+=R-L,L++;
else R--;
}
visit[root]=1;
int i,v,w;
FOR0(i,SZ(g[root]))
{
v=g[root][i].first;
w=g[root][i].second;
if(visit[v]) continue;
V1.clear(); getDeep(v,w,root);
sort(V1.begin(),V1.end());
L=0,R=SZ(V1)-1;
while(L<R)
{
if(V1[L]+V1[R]<=m) ans-=R-L,L++;
else R--;
}
}
FOR0(i,SZ(g[root]))
{
v=g[root][i].first;
if(!visit[v]) solve(v);
}
}
int main()
{
Rush(n)
{
RD(m);
if(!n&&!m) break;
int i;
FOR1(i,n) g[i].clear();
clr(visit,0);
int x,y,z;
FOR1(i,n-1)
{
RD(x,y,z);
g[x].pb(MP(y,z));
g[y].pb(MP(x,z));
}
ans=0; solve(1); PR(ans);
}
}