• 【poj1741】Tree 树的点分治


    题目描述

    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. 

    输入

    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. 

    输出

    For each test case output the answer on a single line.

    样例输入

    5 4
    1 2 3
    1 3 1
    1 4 2
    3 5 1
    0 0

    样例输出

    8


    题目大意

    多组测试数据,每次输入n、m,和一棵n个点的有边权的树,问你满足x到y距离小于等于m的无序点对(x,y)的个数是多少。

    题解

    树的点分治模板题,第一次写

    考虑到路径只有两种情况,一是经过根节点,二是不经过根节点。

    如果不经过根节点,那么一定经过最小公共子树的根节点,可以转化为问题一的子问题。

    于是考虑怎么递归解决问题一。

    对于根节点进行一次dfs,求出deep,并将其从小到大排序。

    避免重复,只需要求出其中deep[x]≤deep[y]且deep[x]+deep[y]≤m的个数。

    用i表示左指针,j表示右指针,i从左向右遍历。

    如果deep[i]+deep[j]≤m,则点对(i,t)(i<t≤j)都符合题意,将j-i加入答案中,并且i++;否则j--。

    然而这样还会重复计算在同一棵子树中的点对,所以再进行下一步dfs之前需要减去重复部分。

    但是这样做会TLE。为什么?因为树可能会退化,导致选择链头时时间复杂度极大。

    于是每次不能固定选择root,而是以重心作为root去处理,这样能保证时间复杂度再O(nlog2n)以下。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 10010
    using namespace std;
    int m , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , si[N] , deep[N] , root , vis[N] , f[N] , sn , d[N] , tot , ans;
    void add(int x , int y , int z)
    {
    	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    }
    void getroot(int x , int fa)
    {
    	f[x] = 0 , si[x] = 1;
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa && !vis[to[i]])
    			getroot(to[i] , x) , si[x] += si[to[i]] , f[x] = max(f[x] , si[to[i]]);
    	f[x] = max(f[x] , sn - si[x]);
    	if(f[root] > f[x]) root = x;
    }
    void getdeep(int x , int fa)
    {
    	d[++tot] = deep[x];
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa && !vis[to[i]])
    			deep[to[i]] = deep[x] + len[i] , getdeep(to[i] , x);
    }
    int calc(int x)
    {
    	tot = 0 , getdeep(x , 0) , sort(d + 1 , d + tot + 1);
    	int i = 1 , j = tot , sum = 0;
    	while(i < j)
    	{
    		if(d[i] + d[j] <= m) sum += j - i , i ++ ;
    		else j -- ;
    	}
    	return sum;
    }
    void dfs(int x)
    {
    	deep[x] = 0 , vis[x] = 1 , ans += calc(x);
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(!vis[to[i]])
    			deep[to[i]] = len[i] , ans -= calc(to[i]) , sn = si[to[i]] , root = 0 , getroot(to[i] , 0) , dfs(root);
    }
    int main()
    {
    	int n , i , x , y , z;
    	while(scanf("%d%d" , &n , &m) && (n || m))
    	{
    		memset(head , 0 , sizeof(head));
    		memset(vis , 0 , sizeof(vis));
    		cnt = 0 , ans = 0;
    		for(i = 1 ; i < n ; i ++ )
    			scanf("%d%d%d" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
    		f[0] = 0x7fffffff , sn = n;
    		root = 0 , getroot(1 , 0) , dfs(root);
    		printf("%d
    " , ans);
    	}
    	return 0;
    }
  • 相关阅读:
    树状数组
    线段树
    最短路(FLOYD)
    欧拉函数
    筛素数
    并查集
    背包方案数问题(礼物)
    [BeijingWc2008]雷涛的小猫
    受欢迎的牛[HAOI2006]
    删除物品[JLOI2013]
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6641720.html
Copyright © 2020-2023  润新知