1. 程式人生 > >基於點分治的樹分治

基於點分治的樹分治

algorithm 計算 開始 tails 論文 continue dfsr size 多少

本文代碼來源:https://blog.csdn.net/yang_7_46/article/details/9966455

本文參考論文來源:https://wenku.baidu.com/view/8861df38376baf1ffc4fada8.html?re=view

基於點分治的樹分治算法

poj1741

  基本步驟:1.一次dfs計算每個點的size

       2.一次dfs找到重心

       3.一次dfs計算每個點的深度

       4.計算以root為重心情況下dis[u]+dis[v]<=k的點對數,u,v是root的兒子

       5.遍歷root的各個子樹,對於每個子樹

          (1):減去dis[u]+dis[v]<=k的點對數,u,v是當前子樹的兒子(為什麽要減去?dis[u]+dis[v]=u,v的路徑長度+2*dis[lca(u,v)],不是u,v的距離)

          (2):開始分治

復雜度計算,分治復雜度O(logN),每次分治最大復雜度O(NlogN),總復雜度O(NlogN*logN)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 10010
using
namespace std; int N,K; int ans,root,Max; struct node{ int v,next,w; }edge[MAXN<<1]; int head[MAXN],tot; int size[MAXN]; int maxv[MAXN]; int vis[MAXN]; int dis[MAXN]; int num; void init(){ tot=ans=0; memset(head,-1,sizeof head); memset(vis,0,sizeof vis); } void addedge(int u,int
v,int w){ edge[tot].v=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } //一次dfs處理子樹的大小 void dfssize(int u,int f){ size[u]=1; maxv[u]=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==f||vis[v]) continue; dfssize(v,u); size[u]+=size[v]; if(size[u]>maxv[u])maxv[u]=size[u]; } } //一次dfs找重心,這裏的r不是重心 void dfsroot(int r,int u,int f){ if(size[r]-size[u]>maxv[u]) maxv[u]=size[r]-size[u]; if(maxv[u]<Max) Max=maxv[u],root=u; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==f||vis[v]) continue; dfsroot(r,v,u); } } //一次dfs求每個點到重心的距離 void dfsdis(int u,int d,int f){ dis[num++]=d; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=f && !vis[v]) dfsdis(v,d+edge[i].w,u); } } int calc(int u,int d){ int ret=0; num=0; dfsdis(u,d,0);//求每個點到根的距離 sort(dis,dis+num); int i=0,j=num-1; while(i<j){ while(dis[i]+dis[j]>K && i<j) j--; ret+=j-i; i++; } return ret; } void dfs(int u){//註意這裏的u並不是重心 Max=N; dfssize(u,0);//求每個子樹的規模 dfsroot(u,u,0);//求重心 ans+=calc(root,0);//求以root為根,dis[u]+dis[v]的點對有多少 vis[root]=1;//把這個點從點集刪掉 for(int i=head[root];i!=-1;i=edge[i].next){ int v=edge[i].v; if(!vis[v]){ ans-=calc(v,edge[i].w); dfs(v); } } } int main(){ while(scanf("%d%d",&N,&K)!=EOF){ if(!N) break; int u,v,w; init(); for(int i=1;i<N;i++){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } dfs(1); printf("%d\n",ans); } return 0; }

基於點分治的樹分治