码迷,mamicode.com
首页 > 其他好文 > 详细

Tree 点分治

时间:2019-08-27 18:53:42      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:i++   排序   bsp   ring   cstring   子节点   stream   ret   add   

 

 

题意:给一颗树,n个点,n-1条边,问有多少对点满足两点之间的距离小于k。码一道题

思路:设这个树的根为p,那么点对(x,y)分为过p点或者在p的子树部分,我们将点对按照所过的根节点进行划分成子问题处理,对于每个以p为根的子树,对于这个树,我们求d数组记录点到根的距离,b数组记录点所在根哪个子节点,之后用a数组记录节点,并把a数组按d排序,之后就可以通过双指针扫描求满足条件点对的个数。

 

//Author:XuHt
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10006;
int n, k, s[N], Ans;
bool v[N], w[N];
int Head[N], Edge[N<<1], Leng[N<<1], Next[N<<1], t;
int ans, pos;
int d[N], b[N], a[N], tot, cnt[N];

void dfs_find(int S, int x) {
    v[x] = 1;
    s[x] = 1;
    int max_part = 0;
    for (int i = Head[x]; i; i = Next[i]) {
        int y = Edge[i];
        if (v[y] || w[y]) continue;
        dfs_find(S, y);
        s[x] += s[y];
        max_part = max(max_part, s[y]);
    }
    max_part = max(max_part, S - s[x]);
    if (max_part < ans) {
        ans = max_part;
        pos = x;
    }
}

void dfs(int x) {
    v[x] = 1;
    for (int i = Head[x]; i; i = Next[i]) {
        int y = Edge[i], z = Leng[i];
        if (v[y] || w[y]) continue;
        ++cnt[b[a[++tot]=y]=b[x]];
        d[y] = d[x] + z;
        dfs(y);
    }
}

bool cmp(int i, int j) {
    return d[i] < d[j];
}

void work(int S, int x) {
    memset(v, 0, sizeof(v));
    ans = S;
    dfs_find(S, x);
    memset(d, 0, sizeof(d));
    memset(cnt, 0, sizeof(cnt));
    memset(v, 0, sizeof(v));
    w[a[tot=1]=b[pos]=pos] = 1;
    ++cnt[pos];
    for (int i = Head[pos]; i; i = Next[i]) {
        int y = Edge[i], z = Leng[i];
        if (v[y] || w[y]) continue;
        ++cnt[a[++tot]=b[y]=y];
        d[y] = z;
        dfs(y);
    }
    sort(a + 1, a + tot + 1, cmp);
    int l = 1, r = tot;
    --cnt[b[a[1]]];
    while (l < r) {
        while (d[a[l]] + d[a[r]] > k) --cnt[b[a[r--]]];
        Ans += r - l - cnt[b[a[l]]];
        --cnt[b[a[++l]]];
    }
    int now = pos;
    for (int i = Head[now]; i; i = Next[i]) {
        int y = Edge[i];
        if (w[y]) continue;
        work(s[y], y);
    }
}

void add(int x, int y, int z) {
    Edge[++t] = y;
    Leng[t] = z;
    Next[t] = Head[x];
    Head[x] = t;
}

void Tree() {
    t = 0;
    memset(Head, 0, sizeof(Head));
    for (int i = 1; i < n; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
    }
    memset(w, 0, sizeof(w));
    Ans = 0;
    work(n, 1);
    cout << Ans << endl;
}

int main() {
    while (cin >> n >> k && n && k) Tree();
    return 0;
}

 

Tree 点分治

标签:i++   排序   bsp   ring   cstring   子节点   stream   ret   add   

原文地址:https://www.cnblogs.com/dongdong25800/p/11419908.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!