[POJ1741]Tree(点分治)

时间:2023-03-08 20:19:31
[POJ1741]Tree(点分治)

树分治之点分治入门

所谓点分治,就是对于树针对点的分治处理

首先找出重心以保证时间复杂度

然后递归处理所有子树

对于这道题,对于点对(u,v)满足dis(u,v)<=k,分2种情况

  1. 路径过当前根
  2. 路径在子树中(递归处理)

那么关键就是如何计算第一种情况

设d[i]表示点i到当前根rt的距离,可以将d数组排序后线性复杂度求

然而此时会有些点对是在同一棵子树中,这些情况要减去

注意每次递归都要找一次重心以保证效率

这样复杂度就是O(nlog2n)

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define Inf 0x7fffffff
#define N 10010
using namespace std; struct info{int to,nex,w;}e[N*2];
int n,k,tot,head[N],d[N],rt,Ans,sum,f[N],sz[N],dep[N];
bool vis[N]; void Link(int u,int v,int w){
e[++tot].nex=head[u];head[u]=tot;e[tot].to=v;e[tot].w=w;
} inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
} void getrt(int u,int fa){
sz[u]=1,f[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(v==fa||vis[v]) continue;
getrt(v,u);
sz[u]+=sz[v];
f[u]=max(f[u],sz[v]);
}
f[u]=max(f[u],sum-sz[u]);
if(f[rt]>f[u]) rt=u;
} void Init(){
tot=0,rt=0,Ans=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=1;i<n;++i){
int u=read(),v=read(),w=read();
Link(u,v,w),Link(v,u,w);
}
sum=n,f[0]=Inf;
getrt(1,0);
} void getdep(int u,int fa){
dep[++dep[0]]=d[u];
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(v==fa||vis[v]) continue;
d[v]=d[u]+e[i].w;
getdep(v,u);
}
} int cal(int u,int cur){
d[u]=cur,dep[0]=0;
getdep(u,0);
sort(dep+1,dep+dep[0]+1);
int t=0;
for(int l=1,r=dep[0];l<r;)
if(dep[l]+dep[r]<=k) t+=r-l,++l;
else --r;
return t;
} void solve(int u){
Ans+=cal(u,0);
vis[u]=1;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(vis[v]) continue;
Ans-=cal(v,e[i].w);
sum=sz[v];
getrt(v,rt=0);
solve(rt);
}
} int main(){
while(~scanf("%d%d",&n,&k)&&n){
Init();
solve(rt);
printf("%d\n",Ans);
}
}