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<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e4 + 15;
int N, lit;
struct Edge
{
    int to, next, len;
    Edge() {}
    Edge(int to, int next, int len): to(to), next(next), len(len) {}
} E[maxn * 2];
int head[maxn], tot;
void initEdge()
{
    for(int i = 0; i <= N + 5; i++) head[i] = -1;
    tot = 0;
}
void addEdge(int u, int v, int len)
{
    E[tot] = Edge(v, head[u], len);
    head[u] = tot++;

    E[tot] = Edge(u, head[v], len);
    head[v] = tot++;
}
int vis[maxn];
int dep[maxn], L, R;
int sz[maxn];
void GetSize(int u, int fa)
{
    sz[u] = 1;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetSize(v, u);
        sz[u] += sz[v];
    }
}
void GetRoot(int u, int fa, int tot, int &rt)
{
    int ma = tot - sz[u];
    if(ma > tot / 2) return ;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetRoot(v, u, tot, rt);
        ma = max(ma, sz[v]);
    }
    if(ma <= tot / 2) rt = u;
}
void GetPath(int u, int fa, int len)
{
    dep[R++] = len;
    for(int k = head[u]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(v == fa || vis[v]) continue;
        GetPath(v, u, len + E[k].len);
    }
}
int GetNum(int L, int R)
{
    int ret = 0;
    int pos = R - 1;
    sort(dep + L, dep + R);
    for(int i = L; i < R; i++)
    {
        while(pos > i && dep[i] + dep[pos] > lit) pos--;
        if(pos > i) ret += pos - i;
        else break;
    }
    return ret;
}
int GetAns(int u)
{
    int ret = 0, rt = 0;
    GetSize(u, -1);
    GetRoot(u, -1, sz[u], rt);//找到重心
    vis[rt] = 1;//重心为分界点
    for(int k = head[rt]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(vis[v]) continue;
        ret += GetAns(v);//不过rt点的个数
    }
    L = R = 0;
    for(int k = head[rt]; ~k; k = E[k].next)
    {
        int v = E[k].to;
        if(vis[v]) continue;
        GetPath(v, rt, E[k].len);
        ret -= GetNum(L, R);
        L = R;
    }
    ret += GetNum(0, R);
    for(int i = 0; i < R; i++)
        if(dep[i] <= lit) ret++;
        else break;
    vis[rt] = 0;
    return ret;
}
int main ()
{
    while(~scanf("%d %d", &N, &lit))
    {
        if(N == 0 && lit == 0) break;
        initEdge();
        for(int i = 1; i < N; i++)
        {
            int u, v, len;
            scanf("%d %d %d", &u, &v, &len);
            addEdge(u, v, len);
        }
        int ans = GetAns(1);
        printf("%d\n", ans);
    }
    return 0;
}
时间: 2024-10-13 16:16:51

POJ 1741 树分治的相关文章

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 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

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 点分治

方法:指针扫描数组 每次选择树的重心作为树根,从树根出发进行一次DFS,求出点到树根的距离,把节点按照与树根的的距离放进数组d,设置两个指针L,R分别从前.后开始扫描,每次满足条件时答案累加R-L.,之后减去子树的满足条件的情况,删除根节点,对其子树继续上述操作,不断累加答案. 代码: #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<v

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, 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)\)内. 具体技巧是:首先选出树的重心,将重心视为根.接着计算出每个结点的深度,以此统计答案.由于子树中可能出现重复

【POJ 1741】 Tree (树的点分治)

Tree 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)