poj 1741 树的点分治(入门)

Tree

Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 18205   Accepted: 5951

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.

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.

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
/*
poj 1741 树的点分治(入门)

problem:
给一棵边带权树,问两点之间的距离小于等于K的点对有多少个

solve:随便找的博客学习下
大致思路,对当前树先求其重心为根节点,然后找出所有点到根节点的距离. 然后就能计算出有多少的的和
<= k. 但是这两个点有可能来自同一个子树,所以在后面计算中减去,就能计算出过当前根节点的所有对.
以同样的方法计算子树的情况,递推下去就行.
重心是为了让最大子树的节点数最小,从而增加效率.
(个人理解)
hhh-2016-08-22 20:00:18
*/
#pragma comment(linker,"/STACK:124000000,124000000")
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <math.h>
#include <map>
#define lson  i<<1
#define rson  i<<1|1
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
#define key_val ch[ch[root][1]][0]
#define inf 0x3FFFFFFFFFFFFFFFLL
using namespace std;
const int maxn = 100010;

struct node
{
    int to,w;
    node() {};
    node(int v,int _w):to(v),w(_w) {};
};

vector<node> q[maxn];
int n,k,s[maxn],f[maxn],root,d[maxn],ans,limit;
int Size;
bool vis[maxn];
vector<int> ta;

void get_root(int now,int fa)
{
    int v;
    s[now] = 1,f[now] = 0;
    for(int i = 0;i < q[now].size();i++)
    {
        if( (v=q[now][i].to) == fa || vis[v])
            continue;
        get_root(v,now);
        s[now] += s[v];
        f[now] = max(f[now],s[v]);
    }
    f[now] = max(f[now],Size - s[now]);
    if(f[now] < f[root]) root = now;
}

void dfs(int now,int fa)
{
    int v;
    ta.push_back(d[now]);
    s[now] = 1;
    for(int i = 0;i < q[now].size();i++)
    {
        if( (v=q[now][i].to) == fa || vis[v])
            continue;
        d[v] = d[now] + q[now][i].w;
        dfs(v,now);
        s[now] += s[v];
    }
}

int cal(int now,int begi)
{
    ta.clear(),d[now] = begi;
    dfs(now,0);
    sort(ta.begin(),ta.end());
    int cnt = 0;
    for(int l = 0,r=ta.size()-1;l<r;)
    {
        if(ta[l] + ta[r] <= limit) cnt += (r-l++);
        else r --;
    }
    return cnt;
}

void work(int now)
{
    int v;
    ans += cal(now,0);
    vis[now] = 1;
    for(int i = 0;i < q[now].size(); i++)
    {
        if(!vis[v = q[now][i].to])
        {
            ans -= cal(v,q[now][i].w);
            f[0] = Size = s[v];
            get_root(v,root = 0);
            work(root);
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&limit) == 2)
    {
        if(!n && !limit)
            break;
        for(int i = 0;i <= n;i++) q[i].clear();
        memset(vis,0,sizeof(vis));

        int a,b,c;
        for(int i = 1;i < n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            q[a].push_back(node(b,c));
            q[b].push_back(node(a,c));
        }
        f[0] = Size = n;
        get_root(1,root = 0);
        ans = 0;
        work(root);
        printf("%d\n",ans);
    }
    return 0;
}

  

时间: 2025-01-05 17:50:36

poj 1741 树的点分治(入门)的相关文章

POJ 1741 树的点分治

题目大意: 树上找到有多少条路径的边权值和>=k 这里在树上进行点分治,需要找到重心保证自己的不会出现过于长的链来降低复杂度 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 #include <algorithm> 6 7 using namespace std; 8 #define N 10005 9 int n ,

POJ 1741 Tree (树上点分治)(楼教主男人八题之一)

题目地址:POJ 1741 树分治第一发! 树分治详情请看漆子超的国家集训队论文,论文传送门 树分治裸题. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #

POJ 1741 树分治(点分治模板题)

POJ 1741 题意:求一棵树中点对<=k的数量. 总结:点分治,搞不太懂..大概敲了一遍 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stac

POJ 1741 Tree (树的点分治入门)

Tree view code#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 10010; int n, k, pre[N], ans, mi, rt, siz[N], num; bool vis[N]; struct edge { int

POJ 1741 树分治

题目链接[http://poj.org/problem?id=1741] 题意: 给出一颗树,然后寻找点对(u,v)&&dis[u][v] < k的对数. 题解: 这是一个很经典的树分治的题.假设我们选择了一个参考点u,那么对于不同的点对(u,v),(u , v)之间的路径有两种情况,经过点u,和不经过点u,加入我算出了没有经过点u的对数,然后把经过点u的加起来就是答案了,很简单,这就是分治的思想.具体看代码. #include<cstdio> #include<c

POJ 1741 树上的点分治

题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 #include <algorithm> 6 7 using namespace std; 8 #de

树的点分治 (poj 1741, 1655(树形dp))

poj 1655:http://poj.org/problem?id=1655 题意: 给无根树,  找出以一节点为根,  使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子树的节点数. 再dfs  找出以某节点为根的最大子树,节点最少. 复杂度(n) /***Good Luck***/ #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cs

poj 1741 楼教主男人八题之一:树分治

http://poj.org/problem?id=1741 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

poj 1741 Tree(树的点分治)

poj 1741 Tree(树的点分治) 给出一个n个结点的树和一个整数k,问有多少个距离不超过k的点对. 首先对于一个树中的点对,要么经过根结点,要么不经过.所以我们可以把经过根节点的符合点对统计出来.接着对于每一个子树再次运算.如果不用点分治的技巧,时间复杂度可能退化成\(O(n^2)\)(链).如果对于子树重新选根,找到树的重心,就一定可以保证时间复杂度在\(O(nlogn)\)内. 具体技巧是:首先选出树的重心,将重心视为根.接着计算出每个结点的深度,以此统计答案.由于子树中可能出现重复