POJ 1741 Tree 树的点分治

时间:2022-10-04 04:24:20

题目大意:给定一棵树,求树上距离不超过k的点对(x,y) (x<y)的数量

男人八题第五题。。。其实没那么难的说。。。比NOI2014最后一题好写多了0.0

首先两个点之间的路径有两种情况:

1.两点之间路径经过根

2.两点之间路径不经过根

首先讨论情况1

我们从根出发进行一次DFS,求出每个点到根的距离,排序,然后扫一遍数组O(n)出解

但其中如果两个点都属于根的同一棵子树,那么这两个点的路径一定是不经过根的,我们还要把这些点对减掉

于是枚举子树,同样把距离排序,扫数组即可

然后讨论情况2 既然这两点间路径不经过根 那么一定经过两点所在的最小子树的根 于是我们枚举子树 递归进行操作1即可

为了防止树退化为链导致的算法时间复杂度的退化,我们每次递归之前O(n)求出树的重心,以重心为根进行处理即可

时间复杂度O(nlog^2n)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 10100
using namespace std;
struct abcd{
int to,f,next;
bool ban;
}table[M<<1];
int head[M],tot=1;
int n,k,ans;
int siz[M],fa[M],dist[M];
int stack[M],top;
void Initialize()
{
memset(head,0,sizeof head);
tot=1;ans=0;
}
void Add(int x,int y,int z)
{
table[++tot].to=y;
table[tot].f=z;
table[tot].next=head[x];
head[x]=tot;
table[tot].ban=0;
}
void Get_Centre_Of_Gravity(int x,int size,int &cg)
{
int i;
bool flag=1;
siz[x]=1;
for(i=head[x];i;i=table[i].next)
if(!table[i].ban)
if(table[i].to!=fa[x])
{
fa[table[i].to]=x;
Get_Centre_Of_Gravity(table[i].to,size,cg);
if(siz[table[i].to]>size>>1)
flag=0;
siz[x]+=siz[table[i].to];
}
if(size-siz[x]>size>>1)
flag=0;
if(flag)
cg=x;
}
void DFS1(int x,int dis)
{
int i;
dist[x]=dis;
for(i=head[x];i;i=table[i].next)
{
if(table[i].ban)
continue;
if(table[i].to==fa[x])
continue;
fa[table[i].to]=x;
DFS1(table[i].to,dis+table[i].f);
}
}
void DFS2(int x)
{
int i;
stack[++top]=dist[x];
for(i=head[x];i;i=table[i].next)
{
if(table[i].ban)
continue;
if(table[i].to==fa[x])
continue;
DFS2(table[i].to);
}
}
int Calculate(int root)
{
int i,j,re=0;
top=0;
DFS2(root);
sort(stack+1,stack+top+1);
for(i=1,j=top;i<=top;i++)
{
for(;j&&stack[j]+stack[i]>k;j--);
re+=j;
}
return re;
}
void Tree_Divide_And_Conquer(int root,int size)
{
int i,cg;
fa[root]=0;
Get_Centre_Of_Gravity(root,size,cg);
siz[fa[cg]]=size-siz[cg];
fa[cg]=0;
DFS1(cg,0);
ans+=Calculate(cg);
for(i=head[cg];i;i=table[i].next)
if(!table[i].ban)
ans-=Calculate(table[i].to);
for(i=head[cg];i;i=table[i].next)
if(!table[i].ban)
table[i].ban=table[i^1].ban=1,Tree_Divide_And_Conquer(table[i].to,siz[table[i].to]);
}
int main()
{
int i,x,y,z;
while(cin>>n>>k,n&&k)
{
Initialize();
for(i=1;i<n;i++)
scanf("%d%d%d",&x,&y,&z),Add(x,y,z),Add(y,x,z);
Tree_Divide_And_Conquer(1,n);
cout<<(ans-n>>1)<<endl;
}
}