题目:
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
题解:
PS:在此引用神犇sdj222555的题解,orz···附上链接:http://blog.csdn.net/sdj222555/article/details/7893862
题意就是求树上距离小于等于K的点对有多少个
n2的算法肯定不行,因为1W个点
这就需要分治。可以看09年漆子超的论文
本题用到的是关于点的分治。
一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心
找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。
最后的复杂度是n logn logn 其中每次快排是nlogn 而递归的深度为logn
心得:
用一句话来总结点分治吧···每次求树的重心,求出经过这个重心的符合条件的路径后将这个点连边删除···
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=10005; int read() { char c; int i=1,f=0; for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar()); if(c=='-') { i=-1; c=getchar(); } for(;c>='0'&&c<='9';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f*i; } int tot,first[N],next[N*5],go[N*5],val[N*5]; int n,k,gravcent,minn; int size[N],maxx[N],dis[N]; bool check[N]; int cnt=0,ans; inline void clear() { tot=ans=0; memset(first,0,sizeof(first)); memset(check,false,sizeof(check)); memset(next,0,sizeof(next)); memset(maxx,0,sizeof(maxx)); } inline void comb(int u,int v,int w) { next[++tot]=first[u],first[u]=tot,go[tot]=v,val[tot]=w; next[++tot]=first[v],first[v]=tot,go[tot]=u,val[tot]=w; } inline void dfssize(int u,int last) { size[u]=1; maxx[u]=0; for(int e=first[u];e;e=next[e]) { int v; if((v=go[e])==last||check[v]==true) continue; dfssize(v,u); size[u]+=size[v]; if(size[v]>maxx[u]) maxx[u]=size[v]; } } inline void dfsgravcent(int root,int u,int last) { if(size[root]-size[u]>maxx[u]) maxx[u]=size[root]-size[u]; if(maxx[u]<minn) minn=maxx[u],gravcent=u; for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==last||check[v]==true) continue; dfsgravcent(root,v,u); } } inline void dfsdis(int u,int d,int last) { dis[++cnt]=d; for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==last||check[v]==true) continue; dfsdis(v,d+val[e],u); } } inline int calc(int u,int d) { int temp=0; cnt=0; dfsdis(u,d,0); sort(dis+1,dis+cnt+1); int i=1,j=cnt; while(i<j) { while(i<j&&dis[i]+dis[j]>k) j--; temp+=j-i; i++; } return temp; } inline void dfs(int u) { minn=n; dfssize(u,0); dfsgravcent(u,u,0); ans+=calc(gravcent,0); check[gravcent]=true; for(int e=first[gravcent];e;e=next[e]) { int v=go[e]; if(!check[v]) { ans-=calc(v,val[e]); dfs(v); } } } int main() { // freopen("a.in","r",stdin); while(true) { n=read(); k=read(); if(n==0&&k==0) break; clear(); for(int i=1;i<n;i++) { int u,v,w; u=read(); v=read(); w=read(); comb(u,v,w); } dfs(1); printf("%d ",ans); } return 0; }