[BZOJ 3784][树上的路径][点分治+堆]

6 篇文章 0 订阅
4 篇文章 0 订阅

[BZOJ 3784][树上的路径][点分治+堆]

题目大意:

给定一个 N 个结点的树,结点用正整数1N编号。每条边有一个正整数权值。用 dist(a,b) 表示从结点 a 到结点b路边上经过边的权值。其中要求 b>a 。将这 n(n+1)2 个距离从大到小排序,输出前 M 个距离值。

思路:

这道题和[BZOJ2006 超级钢琴]类似,没有过的同学可以先切那道题。

http://blog.csdn.net/g1n0st/article/details/56669953

考虑树是一条序列的情况,将dist(1n,n)加入堆,每次取出 dist(i,j) 计入答案后将 dist(i+1,j) 继续加入。

考虑所有情况,我们先对树进行点分治,对于树的每一个重心,计算这个重心到其所管辖的所有节点的距离,按长度降序排序后分别编号为 1n d[i] 表示 dist(i,g) 的长度( g 为当前重心),那么按照一条序列的情况:

对于所有的重心g,我们将 (g,1,1),(g,1,2),,(g,1,n1),(g,1,n) 入堆,每次取出 (g,i,j) ,当:

i<j

ijg

ij

时,我们将 d[i]+d[j] 输出。而当 i+1 仍被重心 g 管辖时,我们在堆中加入(g,i+1,j)

输出 M 个数后算法结束。

//但是貌似每个重心都要加入n个数,总共不会有 N2 个点对入堆吗?

我们将重心分层考虑,总共有 log(n) 层重心,每层有 n 个点(一个点不会在同一层内被两个重心所管辖),所以总共只会有nlog(n)个点对在堆中。

点分复杂度 O(nlogn) ,枚举复杂度 O(klogn) ,总复杂度 O(n+klogn)

代码:
#include <bits/stdc++.h>
const int Maxn = 100010;
using namespace std;
inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c;
    for (; !(c >= '0' && c <= '9'); c = get());
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}
struct Abcd {
    int x, y, type, data;
    friend bool operator < (const Abcd &a, const Abcd &b) {
        return a.data > b.data;
    }
} tmp;
multiset<Abcd> s;
int head[Maxn], sub;
int stk[Maxn], top, dist[Maxn], siz[Maxn], km, num[20];
int d[Maxn][20], belong[Maxn][20], g[Maxn][20], b[Maxn][20];
bool vis[Maxn];
struct Edge {
    int to, nxt, v;
    Edge(void) {}
    Edge(const int &to, const int &nxt, const int &v) : to(to), nxt(nxt), v(v) {}
} edge[Maxn << 1];

inline void add(int a, int b, int v) {
    edge[++sub] = Edge(b, head[a], v), head[a] = sub;
}
inline void dfs(int u, int fa) {
    siz[u] = 1;
    stk[++top] = u;
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (v == fa || vis[v]) continue;
        dfs(v, u);
        siz[u] += siz[v];
    }
}
inline void dg(int u, int fa, int type) {
    for (int i = head[u], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (v == fa || vis[v]) continue;
        d[v][type] = d[u][type] + edge[i].v;
        belong[v][type] = belong[u][type];
        dg(v, u, type);
    }
}
inline bool cmp(int x, int y) {
    return d[x][km] > d[y][km];
}
void solve(int x, int type) {
    top = 0;
    dfs(x, 0);
    int i, j = x, k = 0, t, v;
    while (1) {
        for (i = head[j]; i; i = edge[i].nxt) {
            v = edge[i].to;
            if (!vis[v] && v != k && siz[v] > top >> 1) {
                k = j;
                j = v;
                break;
            }
        }
        if (!i) break;
    }
    for (int i = head[j], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (!vis[v]) {
            belong[v][type] = v;
            d[v][type] = edge[i].v;
            dg(v, j, type);
        }
    }
    for (int i = 1; i <= top; i++) g[stk[i]][type] = j;
    km = type;
    sort(stk + 1, stk + top + 1, cmp);
    for (int i = 1; i <= top; i++) {
        tmp.x = i + num[type];
        tmp.y = 1 + num[type];
        tmp.type = type;
        tmp.data = d[stk[i]][type] + d[stk[1]][type];
        s.insert(tmp);
    }
    for (int i = num[type] + 1; i <= num[type] + top; i++) b[i][type] = stk[i - num[type]];
    num[type] += top;
    vis[j] = 1;
    for (int i = head[j], v; i; i = edge[i].nxt) {
        v = edge[i].to;
        if (!vis[v]) solve(v, type + 1);
    }
}
int n, m;
int main(void) {
    //freopen("b.in", "r", stdin);
    //freopen("b.out", "w", stdout);
    read(n), read(m);
    for (int i = 1, a, b, v; i < n; i++) {
        read(a), read(b), read(v);
        add(a, b, v);
        add(b, a, v);
    }
    solve(1, 0);
    int ans = 0;
    while (m--) {
        while (1) {
            tmp = *s.begin();
            s.erase(s.begin());
            int j = tmp.x, k = tmp.y, l = tmp.type, t = tmp.data;
            if (k + 1 <= num[l] && g[b[j][l]][l] == g[b[k + 1][l]][l]) {
                tmp.data = d[b[j][l]][l] + d[b[k + 1][l]][l];
                tmp.y = k + 1;
                s.insert(tmp);
            }
            if (belong[b[j][l]][l] != belong[b[k][l]][l] && 
                g[b[j][l]][l] == g[b[k][l]][l] && 
                b[j][l] < b[k][l]) {
                ans = t;
                break;
            }
        }
        printf("%d\n", ans);
    }
    //fclose(stdin), fclose(stdout);
    return 0;
}

附Manchery学长的可持久化可并堆(%%%BZOJ rank1)做法:

http://blog.csdn.net/u014609452/article/details/56669183

完。

By g1n0st

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值