poj 1741
题意:给定n个点的一棵树,还有一个k值,问树上任意两节点之间距离小于等于k的有多少对。
思路:先将无根树转化为有根树,可以统计出来每个点到达根的距离,然后就可以将所有的节点深度排序,可以O(n)计算出满足题意的有多少种。
但是这样包含在同一棵子树中的两个节点的贡献值,而且还漏掉了在同一颗子树的两个节点的贡献,因为这样算的是通过根的距离,所以我们就可以将所有的情况都计算出来,然后减去根所有的子树的所贡献出来的情况,这样就是只通过根的情况数了,然后再递归下去算出子树的所有情况加起来就好了,但是有可能树是一条链,所以每一次计算之前,计算一下节点所在树块的重心,然后从重心开始计算,然后重心就可以将当前树块分成很多大小相似的许多小块,最少两块,时间复杂度n*log(n)*log(n)。
#include<cstdio>//树的重心就是重心的子节点的最大节点数最小
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int n,k,head[maxn],cont;
int vis[maxn],siz[maxn],root,f[maxn],ans,d[maxn],tot,deep[maxn],hehe,sn;
/*
sn代表的是当前处理的树块的节点数
siz代表以i为根的子树的节点数
vis标记点是否被用过,也可以理解为用来将树分块
deep代表节点的深度(边权)
*/
struct zp
{
int u,v,w,next;
} node[maxn*2];
void init()
{
cont=0;
hehe=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
void add(int u,int v,int w)
{
node[cont].u=u,node[cont].v=v;
node[cont].w=w;
node[cont].next=head[u];
head[u]=cont++;
}
void get_root(int u,int fa)//求树的重心
{
siz[u]=1;
f[u]=0;
for(int i=head[u]; i!=-1; i=node[i].next)
{
int v=node[i].v;
if(!vis[v]&&v!=fa)
{
get_root(v,u);
siz[u]+=siz[v];
f[u]=max(f[u],siz[v]);
}
}
f[u]=max(f[u],sn-siz[u]);//求出以u为根节点时子节点的最大节点数
if(f[root]>f[u]) root=u;//更新重心
}
void get_deep(int u,int fa)//求出以i为根的书上同一个块到i节点的距离
{
hehe++;
d[tot++]=deep[u];
for(int i=head[u]; i!=-1; i=node[i].next)
{
int v=node[i].v,w=node[i].w;
if(!vis[v]&&v!=fa)
{
deep[v]=deep[u]+w;
get_deep(v,u);
}
}
}
int calc(int u)//计算以u为重心的树块符合题意的节点对数
{
tot=0;
get_deep(u,-1);
sort(d,d+tot);
int i=0,j=tot-1,sum=0;
while(i<j)
{
if(d[i]+d[j]<=k)
{
sum=sum+j-i;
i++;
}
else j--;
}
return sum;
}
void dfs(int u)//树的分治
{
deep[u]=0;
vis[u]=1;//树的点分治标记后将原本的树块分成两块
ans+=calc(u);
for(int i=head[u]; i!=-1; i=node[i].next)
{
int v=node[i].v;
if(!vis[v])
{
deep[v]=node[i].w;
hehe=0;
ans-=calc(v);
sn=hehe;
root=0;
get_root(v,-1);
dfs(root);
}
}
}
int main()
{
while(~scanf("%d%d",&n,&k)&&(n||k))
{
init();
for(int i=1; i<n; i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
root=0;
f[0]=0x3f3f3f3f;
ans=0;
sn=n;
get_root(1,-1);
dfs(root);
printf("%d\n",ans);
}
}
本文介绍了一种解决POJ1741问题的方法,该问题要求计算一棵树中任意两点间距离不超过给定值k的节点对数量。采用树的重心分解技巧,并通过递归计算每棵子树的贡献,最终实现高效求解。

被折叠的 条评论
为什么被折叠?



