Tree
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 30928 | Accepted: 10351 |
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
Sample Output
8
题目大意:
给定一棵n元树,求有多少点对使得这两点的距离小于等于k。
树分治经典题。
以下删改自sdj222555的CSDN博客。
需要分治。可以看09年漆子超的论文。本题用到的是关于点的分治。
一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心。
当然在一次dfs过程中也能做到。另外虽然说不记忆化也能做到,但还是用个son数组记忆下吧。
另外,无向图的dfs不需要像我之前一样遇一个点打个vis标记,dfs定义成dfs(u,pa),保存该点的父亲就不会返回去啦。
找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K。
这里采用的方法就是把所有的距离存在一个数组里(不要忘了重心到自己的),进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。
但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的。所以对每颗子树,把子树内部的满足条件的点对减去。
最后的复杂度是n logn logn 其中每次快排是nlogn 而递归的深度为logn。
代码实现比较复杂,今后有时间还是可以再敲一次。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <queue> #include <deque> #include <stack> #include <map> #include <set> typedef long long ll; const int maxn=10000; const int inf=1000000000; int n,k; int to[maxn*2+5]; int w[maxn*2+5]; int next[maxn*2+5]; int head[maxn+5]; int cnt; void addedge(int a,int b,int c) { to[cnt]=b;w[cnt]=c; next[cnt]=head[a];head[a]=cnt++; } int ans; int mins,root; int son[maxn+5]; int vis[maxn+5]; int depth[maxn+5]; int dis[maxn+5],tot; void getroot(int u,int pa,int num)//求重心 { int maxs=0; son[u]=1; for(int i=head[u];i!=-1;i=next[i]) { int l=to[i]; if(l!=pa&&!vis[l]) { getroot(l,u,num); maxs=std::max(maxs,son[l]); son[u]+=son[l]; } } maxs=std::max(maxs,num-son[u]); if(maxs<mins) { mins=maxs; root=u; } } void getdepth(int u,int pa)//通过深度得出需要的dis值 { for(int i=head[u];i!=-1;i=next[i]) { int l=to[i]; if(l!=pa&&!vis[l]) { depth[l]=depth[u]+w[i]; dis[tot++]=depth[l]; getdepth(l,u); } } } int calc(int u,int pa,int d) { depth[u]=0; tot=0; dis[tot++]=0; getdepth(u,pa); int ret=0; std::sort(dis,dis+tot); int i=0,j=tot-1; while(i<j)//经典的相向搜索 { while(dis[i]+dis[j]+d*2>k&&i<j) j--; ret+=j-i; i++; } return ret; } void dfs(int x,int num) { mins=inf; getroot(x,-1,num);//找重心 ans+=calc(root,-1,0);//计算整棵树符合条件的对数 for(int i=head[root];i!=-1;i=next[i]) { int l=to[i]; if(!vis[l]) ans-=calc(l,root,w[i]);//减去每棵子树符合条件的对数 } vis[root]=1;//打标记 for(int i=head[root];i!=-1;i=next[i]) { int l=to[i]; if(!vis[l]) dfs(l,son[l]);//向子树递归 } } int main() { while(scanf("%d%d",&n,&k),n||k) { memset(head,-1,sizeof(head)); cnt=0; for(int i=1,a,b,c;i<=n-1;i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); addedge(b,a,c); } memset(vis,0,sizeof(vis)); ans=0; dfs(1,n); printf("%d ",ans); } return 0; }