POJ 1741 Tree

难度 尚未评定
Description
给定一颗有个节点的树,每条边有一个权值。树上两个节点和之间的路径长度就是路径上各条边的权值的合。
求树上长度不超过的路径有多少条。
CCYOS
这是一道点分治例题。

  1. 若指定节点为根,则对于节点,树上路径有两类:
    a. 经过根结点。
    b. 包含于的某一子树,不经过。
  2. 对于第二类路径,可以视作每颗子树下的子问题递归处理。
    对于第一类路径,至多由两段组成:和。求解第一类路径时,要记录数组和数组,分别表示根结点到该点的距离和该点属于哪一棵子树。根据题意,
  3. 构建函数,统计在以p为根的树中满足上述要求的点对个数。
  4. 实现方法:
    一 · 树上直接统计
    对于根结点p下的子树为。
    对于中的每个节点,将在子树中满足的节点数量加入答案中即可。
    可建立树状数组求解。
    本做法应用:YALI 0J 2902B 文学
    二·指针扫描数组
    把树上每个点放入数组,并将数组按照值排序。
    使用两个指针从前后扫描数组。
    不难发现在向右扫描的过程中,的范围是从右往左单调递减的。(不能加大数了)
    另外处理一个数组,维护在之间满足的位置的个数。
    则易得当时,答案即为。
    本做法应用:POJ 1741 Tree
  5. 总结点分治算法过程:
    a.找到树的中心p作为根结点。
    b.从p进行dfs,求出rtd数组。
    c.clac(p)统计答案。
    d.删除根结点p,对p的每颗子树进行a - d。

code

#include 
#include
#include  
#include  
using  namespace std;  
struct EDGE{  int to,next;  long  long w;  }edg[80005];  
int n,tot,root,limit,siz,vis[40005],head[40005],size[40005];  
long  long k,ans,l,r;  
long que[40005],dis[40004];  
inline  void adde(int x,int y,int w){ edg[++tot].to = y; edg[tot].w = w; edg[tot].next  = head[x]; head[x]  = tot;  } 
inline  void getroot(int p,int fa){
  size[p]  =  1;  int num =  0;//从p点出发的子树大小 
  for(int i = head[p];i;i = edg[i].next){
    int to = edg[i].to;  
    if(fa == to||vis[to])continue; 
    getroot(to,p); size[p]  += size[to]; 
    num = max(num,size[to]);  
  } 
  num = max(num,siz - size[p]);  
  if(num < limit){//limit:当前最小 
    limit = num; root = p;  
  }  
}  
inline  void getdis(int p,int fa){ 
  que[++r]  = dis[p];    
  for(int i = head[p];i;i = edg[i].next){
    int to = edg[i].to;
    if(to == fa||vis[to])continue;
    dis[to]  = dis[p]  + edg[i].w; getdis(to,p);  
  }  
}  
inline  long  long clac(int p,int v){
  r =  0; dis[p]  = v;  
  long  long sum =  0; 
  getdis(p,0); 
  l =  1; 
  sort(que +  1,que + r +  1);  
  while(l < r){
    if(que[l]  + que[r]  <= k)sum += r - l,++l;  
    else r--;  
  }  
  return sum;  
}  
inline  void dfs(int p){
  ans += clac(p,0); vis[p]  =  1;//将p点作为根结点是否处理完毕
  for(int i = head[p];i;i = edg[i].next){
    int to = edg[i].to;
    if(vis[to])continue;
    ans -= clac(to,edg[i].w);
    siz = size[to];
    limit =  2147483647;
    getroot(to,0);
    dfs(root);  
  }  
}  
inline  void init(){ 
  tot =  0;ans =  0;  
  for(int i =  1;i <= n;++i)head[i]  =  0,vis[i]  =  0;  
}  
int main(){ 
  scanf("%d%lld",&n,&k);  
  int x,y;  long  long w;  
  while(n&&k){  for(int i =  1;i < n;++i){   
    scanf("%d%d%lld",&x,&y,&w); 
    adde(x,y,w); adde(y,x,w);  
  } 
  limit =  2147483647;// 
  siz = n;//对于第一棵子树的大小限制     
  getroot(1,0);//找到树的重心,更新root 
  dfs(root);//从root开始递归统计更新答案 
  printf("%lld\n",ans); init(); 
  scanf("%d%lld",&n,&k);  
  }  
return  0;  
}

你可能感兴趣的:(POJ 1741 Tree)