题目链接:http://poj.org/problem?id=1741
大致题意:
给定一棵N(1<=N<=10000)个结点的带权树,定义dist(v,u)为
vu,两点间的最短路径长度,路径的长度定义为路径上所有边的权和。再给定一个K(1<=K<=1e9)如果对于不同的两个结点a,b,如果满
足dist(a,b)<=K则称(a,b)为合法点对。
求合法点对个数。
大致题解:(参考国家集训队论文:http://wenku.baidu.com/link?url=7KOPn20aLvKK5PqDmuLjIyj4sqZ6CL1H9qP__JSGvX-AWgX7LR6gC-BZ3PTVCP2ojBHxKZcJ5U3csiRjuspqcoFJfswO7JaEIQyKlxwUzBi)
如果使用普通的DFS遍历,时间复杂度高达O(N^2),而使用时
间复杂度为O(NK)的动态规划,更是无法在规定时限内出解的。
我们知道一条路径要么过根结点,要么在一棵子树中,这启发了我们可以使用分治算法。
路径在子树中的情况只需递归处理即可,下面我们来分析如何处理路径过根结点的情况。
记Depth(i)表示点i到根结点的路径长度,Belong(i)=X (X为根结点的某个儿子,且结点i在以X为根的子树内)。那么我们要统计的就是:
满足Depth(i)+Depth(j)<=K且Belong(i)!=Belong(j)的个数
=满足Depth(i)+Depth(j)<=K的(i,j)个数 –满足Depth(i)+Depth(j)<=K且Belong(i)!=Belong(j)的(i,j)个数
而对于这两个部分,都是要求出满足Ai+Aj<=k的(i,j)对数。将A排序后利用单调性我们很容易得出一个O(N)的算法,所以我们可以用O(NlogN)的时间来解决这个问题。
综上,此题使用树的分治算法时间复杂度为O(Nlog^2N).
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 1e4+7;
const int MAXM = 1e5+7;
const double eps = 1e-5;
int n,k;
struct node
{
int v,next,cost;
} edge[MAXM];
int head[MAXM],index;
void add_edge(int u,int v,int c)
{
edge[index].v=v;
edge[index].cost=c;
edge[index].next=head[u];
head[u]=index++;
}
int vis[MAXN],siz[MAXN],mxe[MAXN],dis[MAXN];
int mmin,root,ans,num;
void get_size(int u,int father)
{
siz[u]=1;
mxe[u]=0;
for(int i=head[u]; i+1; i=edge[i].next)
{
int v=edge[i].v;
if(v!=father && !vis[v])
{
get_size(v,u);
mxe[u]=max(mxe[u],siz[v]);
siz[u]+=siz[v];
}
}
}
int p;
void get_root(int u,int father)//寻找树的重心
{
if(siz[p]-siz[u]>mxe[u])mxe[u]=siz[p]-siz[u];
if(mxe[u]<mmin)mmin=mxe[u],root=u;
for(int i=head[u]; i+1; i=edge[i].next)
{
int v=edge[i].v;
if(v!=father && !vis[v])
get_root(v,u);
}
}
void get_dis(int u,int father,int c)
{
dis[num++]=c;
for(int i=head[u]; i+1; i=edge[i].next)
{
int v=edge[i].v;
if(v!=father && !vis[v])
get_dis(v,u,edge[i].cost+c);
}
}
int cal(int u,int c)
{
int temp=0;
num=0;
get_dis(u,0,c);
sort(dis,dis+num);
int l=0,r=num-1;
while(l<r)
{
while(dis[l]+dis[r]>k && l<r)r--;
temp+=r-l;
++l;
}
return temp;
}
void DFS(int u)
{
mmin=n;
get_size(u,0);
p=u;
get_root(u,0);
ans+=cal(root,0);
vis[root]=1;
for(int i=head[root]; i+1; i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
ans-=cal(v,edge[i].cost);//减掉(i,j)同在子树内的情况
DFS(v);
}
}
}
int main()
{
int a,b,c;
while(~scanf("%d%d",&n,&k))
{
if(!n)break;
index=ans=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=1; i<n; ++i)
{
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
}
DFS(1);
printf("%d\n",ans);
}
return 0;
}