poj1741 树上的点分治
题意:
一棵10000个点的树,每条边的长不超过1000,给定一个值k,问距离不超过k的点对数有多少。(多组数据)
输入样例:
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
输出样例:
8
————————————————————————————————————————————————————————————————————————————
这个题目是在CODEVS 2756 树上的路径 的学习过程中看到别人的题解的过程中看到的,这个题相对简单,是做那个题的基础。基本上是人家的代码,和抄也差不了多少。是学习树上的点分治的一个基础题。
根据一个大神漆子超的论文,树上的分治可以分为点分治,边分治和链分治。点分治最差log,边分治最差n。而链分治是最好的,不过没有学会,有空再从网上找来学习一下,就是只学习点皮毛,对自己做题思路也是一种扩展。
点分治,就是在树中去掉一个点,从而使原来的树变成更小的树,从而达到分治的目的。为了防止极差情况的出现,提高分治的效率,最好的分治点是树的重心(去掉该点后,所得最大子树最小)。所在就有了点分治的方法。
1、深搜,得到各个点为根的子树的大小和各个点的最大子树的大小
2、二次深搜,得到去掉各个点得到的最大子树的大小,从而通过遍历得到重心。
3、应用问题与重心的关系进行分治。
(本题中:
3.1、计算通过重心且不超过k的点对数(包含重边)。
3.2、减去过重心且不超过k的点对数(即有重边的点对数)。
3.3、标明已查到的重心(vis),在新生成的子树中重新进行上述操作,完成分治。
)
边分治,没有做过,不过据漆子超说,是去掉一条过,把树分成2个树,完成分治。个人愚见,如果不是星型那种极个别的情况也可以实现log。
————————————————————————————————————————————————————————————————————————————
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 const int maxn=10010; 8 9 int n,k,ans; 10 struct edge 11 { 12 int u,v,w,next; 13 }e[2*maxn]; 14 int head[maxn],js,jst,mi,root; 15 int siz[maxn],mx[maxn],dis[maxn]; 16 bool vis[maxn]; 17 void init() 18 { 19 memset(head,0,sizeof(head)); 20 memset(vis,0,sizeof(vis)); 21 js=0; 22 ans=0; 23 } 24 void readint(int &x) 25 { 26 int f=1; 27 char c=getchar(); 28 for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-f; 29 x=0; 30 for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0'; 31 x=x*f; 32 } 33 void addage(int u,int v,int w) 34 { 35 e[++js].u=u;e[js].v=v;e[js].w=w; 36 e[js].next=head[u];head[u]=js; 37 } 38 void dfssize(int u,int fa) 39 { 40 siz[u]=1;mx[u]=0; 41 for(int i=head[u];i;i=e[i].next) 42 { 43 int v=e[i].v; 44 if(v!=fa && !vis[v]) 45 { 46 dfssize(v,u); 47 siz[u]+=siz[v]; 48 if(siz[v]>mx[u])mx[u]=siz[v]; 49 } 50 } 51 } 52 void dfsroot(int r,int u,int f) 53 { 54 if(siz[r]-siz[u]>mx[u])mx[u]=siz[r]-siz[u]; 55 if(mx[u]<mi) 56 { 57 mi=mx[u]; 58 root=u; 59 } 60 for(int i=head[u];i;i=e[i].next) 61 { 62 int v=e[i].v; 63 if(v!=f &&!vis[v]) 64 { 65 dfsroot(r,v,u); 66 } 67 } 68 } 69 void dfsdis(int u,int d,int f) 70 { 71 dis[jst++]=d; 72 for(int i=head[u];i;i=e[i].next) 73 { 74 int v=e[i].v; 75 if(v!=f && !vis[v]) 76 dfsdis(v,d+e[i].w,u); 77 } 78 } 79 int calc(int u,int d) 80 { 81 int anst=0; 82 jst=0; 83 dfsdis(u,d,0); 84 sort(dis,dis+jst); 85 int i=0,j=jst-1; 86 while(i<j) 87 { 88 while(dis[i]+dis[j]>k && i<j)j--; 89 anst+=j-i; 90 i++; 91 } 92 return anst; 93 } 94 void dfs(int u) 95 { 96 mi=n; 97 dfssize(u,0); 98 dfsroot(u,u,0); 99 ans+=calc(root,0); 100 vis[root]=1; 101 for(int i=head[root];i;i=e[i].next) 102 { 103 int v=e[i].v; 104 if(!vis[v]) 105 { 106 ans-=calc(v,e[i].w); 107 dfs(v); 108 } 109 110 } 111 112 } 113 int main() 114 { 115 while(scanf("%d%d",&n,&k)==2) 116 { 117 if(!n && !k)break; 118 init(); 119 for(int u,v,w,i=1;i<n;i++) 120 { 121 readint(u);readint(v);readint(w); 122 addage(u,v,w);addage(v,u,w); 123 } 124 dfs(1); 125 printf("%d\n",ans); 126 } 127 return 0; 128 }