zoukankan      html  css  js  c++  java
  • POJ 1741 Tree 树的分治(点分治)

    题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数。


    思路:树的点分治。利用递归和求树的重心来解决这类问题。由于满足题意的点对一共仅仅有两种:

    1.在以该节点的子树中且不经过该节点。

    2.路径经过该节点。

    对于第一种点,我们递归处理;另外一种点。我们能够将全部子树的节点到这个子树的根节点的距离处理出来,然后排序处理出满足要求的点对的个数。

    依照正常的树的结构来切割子树,这种做法的时间复杂度肯定是不好看的,为了让子树大小尽量同样。我们每次处理这个子树前找到这个子树的重心,把这个重心当为根,然后在切割子树,这样时间复杂度最坏会降到O(nlog^2n)。


    CODE:


    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define MAX 20010
    #define INF 0x3f3f3f3f
    using namespace std;
    
    int points,edges,k;
    int head[MAX],total;
    int next[MAX << 1],aim[MAX << 1],length[MAX << 1];
    
    int cnt[MAX],c;  			//每一个子树中经过根节点的满足条件的对数
    int size[MAX],_size,dis[MAX],p;
    int _total;
    bool v[MAX];
    
    inline void Initialize();
    inline void Add(int x,int y,int len);
    void Work(int x);
    void GetRoot(int x,int last);
    inline int Count(int x,int len);
    void GetDis(int x,int last,int len);
    
    int main()
    {
    	while(scanf("%d%d",&points,&k),points + k) {
    		Initialize();
    		for(int x,y,z,i = 1;i < points; ++i) {
    			scanf("%d%d%d",&x,&y,&z);
    			Add(x,y,z),Add(y,x,z);
    		}
    		Work(1);
    		int ans = 0;
    		for(int i = 1;i <= points; ++i)
    			ans += cnt[i];
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
    inline void Initialize()
    {
    	total = 0;
    	memset(head,0,sizeof(head));
    	memset(v,false,sizeof(v));
    }
    
    inline void Add(int x,int y,int len)
    {
    	next[++total] = head[x];
    	aim[total] = y;
    	length[total] = len;
    	head[x] = total;
    }
    
    void Work(int x)
    {
    	_size = INF;
    	_total = size[x] ?

    size[x]:points; GetRoot(x,0); x = c; v[x] = true; cnt[x] = Count(x,0); for(int i = head[x];i;i = next[i]) { if(v[aim[i]]) continue; cnt[x] -= Count(aim[i],length[i]); Work(aim[i]); } } void GetRoot(int x,int last) { size[x] = 1; int max_size = 0; for(int i = head[x];i;i = next[i]) { if(v[aim[i]] || aim[i] == last) continue; GetRoot(aim[i],x); size[x] += size[aim[i]]; max_size = max(max_size,size[aim[i]]); } max_size = max(max_size,_total - size[x]); if(max_size < _size) _size = max_size,c = x; } inline int Count(int x,int len) { int re = 0; p = 0; GetDis(x,0,len); sort(dis,dis + p); int l = 0,r = p - 1; while(l < r) { if(dis[l] + dis[r] <= k) re += (r - l),l++; else r--; } return re; } void GetDis(int x,int last,int len) { dis[p++] = len; for(int i = head[x];i;i = next[i]) { if(aim[i] == last || v[aim[i]]) continue; GetDis(aim[i],x,len + length[i]); } }



  • 相关阅读:
    最长上升子序列问题总结
    Problem C
    Problem C
    Problem P
    Problem P
    Problem H
    Problem H
    Problem D
    Problem D
    Linux系统调用--getrlimit()与setrlimit()函数详解
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6939781.html
Copyright © 2011-2022 走看看