题意就是求树上距离小于等于K的点对有多少个
n2的算法肯定不行,因为1W个点
这就需要分治。可以看09年漆子超的论文
本题用到的是关于点的分治。
一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心
找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。
最后的复杂度是n logn logn 其中每次快排是nlogn 而递归的深度是logn
友情链接:~~http://blog.csdn.net/sdj222555/article/details/7893862
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <string> 5 #include <cstdio> 6 #include <cmath> 7 #include <queue> 8 #include <map> 9 #include <set> 10 #define eps 1e-5 11 #define MAXN 11111 12 #define MAXM 55555 13 #define INF 1000000000 14 using namespace std; 15 struct EDGE 16 { 17 int v, next, w; 18 }edge[MAXM]; 19 int head[MAXN], e; 20 int n, k, vis[MAXN], ans, root, num; 21 void init() // 清空初始值 22 { 23 memset(vis,0,sizeof(vis)); 24 memset(head,-1,sizeof(head)); 25 e=ans=0; 26 } 27 void add(int u,int v,int w) // 边表 加边 28 { 29 edge[e].v=v; 30 edge[e].w=w; 31 edge[e].next=head[u]; 32 head[u]=e++; 33 } 34 int mx[MAXN], size[MAXN], mi, dis[MAXN]; 35 void dfssize(int u, int fa) //处理以u为顶的子树的大小 fa是其父节点 36 { 37 size[u] = 1; 38 mx[u] = 0; 39 for(int i = head[u]; i != -1; i = edge[i].next) 40 { 41 int v = edge[i].v; 42 if(v != fa && !vis[v]) 43 { 44 dfssize(v, u); 45 size[u] += size[v]; 46 if(size[v] > mx[u]) mx[u] = size[v]; 47 } 48 } 49 } 50 void dfsroot(int r,int u,int fa)//求重心所谓重心是指删去该点后 51 { //所形成的子树的节点数最大的最小 52 if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u]; 53 if(mx[u]<mi) mi=mx[u],root=u; 54 for(int i=head[u];i!=-1;i=edge[i].next) 55 { 56 int v=edge[i].v; 57 if(v!=fa&&!vis[v]) dfsroot(r,v,u); 58 } 59 } 60 void dfsdis(int u, int d, int fa) //求所有点到达重心的距离 即dis 61 { 62 dis[num++] = d; 63 for(int i = head[u]; i != -1; i = edge[i].next) 64 { 65 int v = edge[i].v; 66 if(v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u); 67 } 68 } 69 int calc(int u,int d) 70 { 71 int ret=0; 72 num=0; 73 dfsdis(u,d,0); 74 sort(dis,dis+num); 75 int i=0,j=num-1; 76 while(i<j) //经典 77 { 78 while(dis[i]+dis[j] > k && i < j) j--; 79 ret+=j-i; 80 i++; 81 } 82 return ret; 83 } 84 void dfs(int u) 85 { 86 mi = n; 87 dfssize(u, 0); // 子树大小 88 dfsroot(u, u, 0); // 重心 求完之后 root 即为重心 89 ans+=calc(root,0);//经过root的并且满足要求的点对数(这时候会出现重边) 90 vis[root]=1; 91 for(int i = head[root]; i != -1; i = edge[i].next) 92 { 93 int v=edge[i].v; 94 if(!vis[v]) 95 { 96 ans-=calc(v,edge[i].w);//v是root的son 以v,edge[i].w继续向下深搜 97 //若这样还是满足要求(经过son并且满足要求的点对数, 98 //这就是重边的情况,这时将它减掉) 99 dfs(v);//继续处理root的son,情况同上 100 } 101 } 102 } 103 int main() 104 { 105 while(scanf("%d%d", &n, &k) != EOF) 106 { 107 if(!n && !k) break; // 不到终止条件 108 init(); 109 int u, v, w; 110 for(int i = 0; i < n - 1; i++) 111 { 112 scanf("%d%d%d", &u, &v, &w); 113 add(u, v, w); 114 add(v, u, w); 115 } 116 dfs(1); 117 printf("%d ", ans); 118 } 119 return 0; 120 }
附上图解:(图是手绘的,见谅)
当一遍处理的时候 ,root等于1,这时候ans会把5-1-2这样的点对加进去,所以我们以2,dis[2](dis[2]=1)再深搜,若是再这样的条件下还满足条件(过2节点,【此时dis值没变】,还满足小于等于7,说明他就是重边,应该减掉,而对于2-5这样合法的会在dfs[2]的时候,处理好)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 #define MAXN 11111 7 #define MAXM 55555 8 struct EDGE{ 9 int v,w,next; 10 }edge[MAXM]; 11 int n,k,vis[MAXN],mx[MAXN],e,ans; 12 int root,num,dis[MAXN],head[MAXN],size[MAXN],mi; 13 void init(){ 14 memset(vis,0,sizeof(vis)); 15 memset(head,-1,sizeof(head)); 16 ans=e=0; 17 } 18 void add(int u,int v,int w){ 19 edge[e].v=v;edge[e].w=w; 20 edge[e].next=head[u];head[u]=e++; 21 } 22 void dfssize(int u,int fa){ 23 size[u]=1;mx[u]=0; 24 for(int i=head[u];i!=-1;i=edge[i].next){ 25 int v=edge[i].v; 26 if(v!=fa&&!vis[v]){ 27 dfssize(v,u); 28 size[u]+=size[v]; 29 if(size[v]>mx[u]) mx[u]=size[v]; 30 } 31 } 32 } 33 void dfsroot(int r,int u,int fa){ 34 if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u]; 35 if(mx[u]<mi){ mi=mx[u];root=u; } 36 for(int i=head[u];i!=-1;i=edge[i].next){ 37 int v=edge[i].v; 38 if(v!=fa&&!vis[v]){ 39 dfsroot(r,v,u); 40 } 41 } 42 } 43 void dfsdis(int u,int d,int fa){ 44 dis[num++]=d; 45 for(int i=head[u];i!=-1;i=edge[i].next){ 46 int v=edge[i].v; 47 if(v!=fa&&!vis[v]){ 48 dfsdis(v,d+edge[i].w,u); 49 } 50 } 51 } 52 int calc(int u,int d){ 53 int ret=0; 54 num=0; 55 dfsdis(u,d,0);sort(dis,dis+num); 56 int i=0,j=num-1; 57 while(i<j){ 58 while(dis[i]+dis[j]>k&&i<j) j--; 59 ret+=j-i; 60 i++; 61 } 62 return ret; 63 } 64 void dfs(int u) 65 { 66 mi=n; 67 dfssize(u,0);dfsroot(u,u,0); 68 ans+=calc(root,0); 69 vis[root]=1; 70 for(int i=head[root];i!=-1;i=edge[i].next){ 71 int v=edge[i].v; 72 if(!vis[v]){ 73 ans-=calc(v,edge[i].w); 74 dfs(v); 75 } 76 } 77 } 78 int main() 79 { 80 while(scanf("%d%d",&n,&k)!=EOF) 81 { 82 if(!n&&!k) break; 83 init();int u,v,w; 84 for(int i=1;i<n;i++){ 85 scanf("%d%d%d",&u,&v,&w); 86 add(u,v,w);add(v,u,w); 87 } 88 dfs(1); 89 printf("%d ",ans); 90 } 91 return 0; 92 }
另附网址:09年漆子超论文 提取密码:95tu