1741 Tree (树的分治,点分治)

原创 2013年12月03日 18:19:05

参考:

http://hi.baidu.com/strongoier/item/fe47a4191c18a37c1009b515

http://hi.baidu.com/shuxk/item/2bd327977576038159146161

http://hi.baidu.com/gugugupan/item/f29befcd12accc67f7c95db9

给出N(1 <= N <= 10000)个结点的树,求使得路径u -> v长度不超过k的点对(u, v)的个数。

点分治法:每次找出分治点,求经过其的路径的点对,子树递归处理即可。

会有重复值,需要减去子节点相关的一些值

注意技巧:

删除点的办法。adj[r ^ 1].mk = false;

找出分支点的方法。(与直径中心的差别?)

算法就是基于树的分治,对于每个树,先要选定一个根节点,这个根节点要保证它的深度尽量小,然后才可以保证时间复杂度。选根的过程大致就是一次DFS,找到以 I 为根节点时儿子最多的子树的儿子树最少的 I 就可以作为根了。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

#define FE(i, a, b) for(int i = (a); i <= (b); ++i)
#define FD(i, b, a) for(int i = (b); i >= (a); --i)
#define REP(i, N) for(int i = 0; i < (N); ++i)
#define CLR(a, v) memset(a, v, sizeof(a))
#define PB push_back
#define MP make_pair

#define RI(n) scanf("%d", &n)
#define RIII(a, b, c) scanf("%d%d%d",&a, &b, &c)

typedef long long LL;
const double eps = 1e-10;
const int maxn = 10010;

int a[maxn], an;
int sz[maxn], szn;
int f[maxn], d[maxn];
int n, m;
int cur;///curroot
int ans;

struct Edge {
    int to, w, next;
    bool mk;
}adj[maxn * 2];
int adj_tot, head[maxn];
void adj_add(int x, int y, int w)
{
    adj[adj_tot].to = y; adj[adj_tot].w = w;
    adj[adj_tot].next = head[x];
    adj[adj_tot].mk = true;
    head[x] = adj_tot++;
}

void find_root(int u, int fa)///找分治点
{
    sz[u] = 1;///
    f[u] = 0;
    for (int r = head[u]; ~r; r = adj[r].next)
    {
        int v = adj[r].to;
        if (adj[r].mk && v != fa)
        {
            find_root(v, u);
            sz[u] += sz[v];
            f[u] = max(f[u], sz[v]);
        }
    }
    f[u] = max(f[u], szn - sz[u]);
    if (f[u] < f[cur]) cur = u;
}

void dfs(int u, int fa)
{
    sz[u] = 1;///
    a[an++] = d[u];
    for (int r = head[u]; ~r; r = adj[r].next)
    {
        int v = adj[r].to;
        if (adj[r].mk && v != fa)
        {
            d[v] = d[u] + adj[r].w;
            dfs(v, u);
            sz[u] += sz[v];
        }
    }
}

int calc(int x, int initval)
{
    int ret = an = 0;
    d[x] = initval;
    dfs(x, 0);
    sort(a, a + an);
    int l = 0, r = an - 1;
    for (; l < r;)///!!!
    {
        if (a[r] + a[l] <= m)
            ret += r - l++;
        else r--;
    }
    return ret;
}

void solve(int u)
{
    ans += calc(u, 0);
    for (int r = head[u]; ~r; r = adj[r].next)
    {
        int v = adj[r].to;
        if (adj[r].mk)
        {
            adj[r ^ 1].mk = false;///!!!
            ans -= calc(v, adj[r].w);///???

            f[0] = szn = sz[v];
            find_root(v, cur = 0);
            solve(cur);
        }
    }
}

int main ()
{
    int x, y, w;
    while (scanf("%d%d", &n, &m) != EOF && n + m )
    {
        adj_tot = 0; CLR(head, -1);
        ans = 0;
        for (int i = 0; i < n - 1; i++)
        {
            scanf("%d%d%d", &x, &y, &w);
            adj_add(x, y, w); adj_add(y, x, w);
        }
        f[0] = szn = n;
        find_root(1, cur = 0);
        solve(cur);
        printf("%d\n", ans);
    }
    return 0;
}


POJ 1741 Tree(树的点分治、树形dp、男人八题)

【题意】求树上距离小于等于K的点对有多少个 n有10000,所以必须卡到nlogn附近。变得尤为麻烦。 首先,一对符合要求的点,它们经过的路径中一定可以找到一个根节点。 每次我们都找以这个根为“...
  • AngOn823
  • AngOn823
  • 2016-08-24 16:55:41
  • 682

树分治(点分治模板)poj-1741 Tree

首先讲解一下树分治,以下的内容转自:http://blog.sina.com.cn/s/blog_6d5aa19a0100o73m.html 对于一棵有根树, 树中满足要求的一个数对所对应的一条...
  • u010660276
  • u010660276
  • 2015-04-07 15:22:52
  • 5456

[codeforces 161D] Distance in Tree(树的点分治)

题目:http://codeforces.com/problemset/problem/161/DD. Distance in Tree time limit per test 3 seconds...
  • ALPS233
  • ALPS233
  • 2016-05-13 10:40:17
  • 1195

树上基于点的分治

Preface之前一直没有时间弄这个,直到前段时间模拟赛出了一道点分治的题目,然后就眼睁睁看着人家切自己不会做。唉~Text其实这玩意是蛮水的一个东西,对于当前递归到的子树,选出一个点为根,然后分治这...
  • hzj1054689699
  • hzj1054689699
  • 2016-09-09 20:10:14
  • 373

poj 1741 Tree(树形DP+分治)难

1、http://poj.org/problem?id=1741 2、题目大意: 给一棵树,dist(u,v)是指的uv两个顶点之间的最短距离,现在给出一个k值, 求在这棵树上有多少对(u,v)...
  • sdjzping
  • sdjzping
  • 2014-01-05 16:34:21
  • 2074

SPOJ QTREE4 Query on a tree IV(边分治)

qtree
  • u013849646
  • u013849646
  • 2016-03-31 23:29:57
  • 601

BZOJ1095 & 动态点分治(好像应该叫点分树?)学习笔记

神一般的点分治
  • QWsin
  • QWsin
  • 2017-04-18 15:21:04
  • 1621

素数路径 Prime Distance On Tree 点分治+FFT

对于这个题,没有什么好用的数学性质,那么考虑统计所有长度的路径条数 路径统计问题不难想到点分治之后统计每个点的路径条数即可 假设之前子树到根的距离集合存为B,其中B[i]表示到根距离为i的有多少条...
  • liuguangzhe1999
  • liuguangzhe1999
  • 2016-04-13 19:30:26
  • 744

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

题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离 思路:树的点分治。利用递归和求树的重心来解决这类问题。因为满足题意的点对一共只有两种: 1.在以该节点的子树中且不经过该节点...
  • jiangyuze831
  • jiangyuze831
  • 2014-10-11 16:00:47
  • 668

【BZOJ】1468 Tree 点分治

题目传送门 这题就是一道点分治的模板题,就当是立学习了点分治的Flag了。 点分治,就是把分治的思想转移到树上,然后对于任意两个节点的路径过当前子树的情况进行特殊讨论。 这道题是求任意两点间的距...
  • lyfsb
  • lyfsb
  • 2017-05-25 20:53:02
  • 314
收藏助手
不良信息举报
您举报文章:1741 Tree (树的分治,点分治)
举报原因:
原因补充:

(最多只允许输入30个字)