树分治之点分治入门
所谓点分治,就是对于树针对点的分治处理
首先找出重心以保证时间复杂度
然后递归处理所有子树
对于这道题,对于点对(u,v)满足dis(u,v)<=k,分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 ",Ans); } }