BZOJ 3784|树上的路径|点分治|堆|RMQ

10 篇文章 0 订阅
9 篇文章 0 订阅

求前M个路径,使路径权最小。

NOI 2010 超级钢琴类似。。。

#include <queue>
#include <cstdio>
#include <cstring>
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 50005, M = N * 2, S = 2000005;
using namespace std;
int bin[20], lb[N];
int f[N], sz[N], dis[N], sum, rt;
int sk[S], top, mx[S][20];
pair<int, int> pa[S];
bool vis[N];
int h[N], p[M], v[M], w[M], cnt, n;
void init_rmq(int n) {
    int i, j, t1, t2;
    bin[0] = 1; FOR(i,1,19) bin[i] = bin[i - 1] << 1;
    lb[0] = -1; FOR(i,1,n) lb[i] = lb[i >> 1] + 1;
    FOR(i,1,top) mx[i][0] = i;
    FOR(i,1,lb[n]) FOR(j,1,top) if(j + bin[i] <= top) {
        t1 = mx[j][i - 1], t2 = mx[j + bin[i - 1]][i - 1];
        mx[j][i] = sk[t1] > sk[t2] ? t1 : t2;
    }
}
int query(int l, int r) {
    if (l == r) return l;
    int t = lb[r - l + 1];
    int t1 = mx[l][t], t2 = mx[r - bin[t] + 1][t];
    return sk[t1] > sk[t2] ? t1 : t2;
}
struct Data { int x, l, r, t;
Data(int a,int b,int c):l(a),r(b),x(c),t(query(a,b)){}
};
priority_queue<Node> q;
bool operator< (Data a, Data b) {
    return sk[a.t] + sk[a.x] < sk[b.t] + sk[b.x];
}
void add(int a, int b, int c) {
    v[++cnt] = b; p[cnt] = h[a]; w[cnt] = c; h[a] = cnt;
}
void root(int x, int fa) {
    f[x] = 0; sz[x] = 1;
    for (int i = h[x]; i; i = p[i])
        if (v[i] != fa && !vis[v[i]]) {
            root(v[i], x);
            sz[x] += sz[v[i]];
            f[x] = max(f[x], sz[v[i]]);
        }
    f[x] = max(f[x], sum - sz[x]);
    if (f[x] < f[rt]) rt = x;
}
int get_root(int x, int fa, int sz) {
    rt = 0; sum = sz; f[0] = sz + 1;
    root(x, fa); return rt;
}
void depth(int x, int fa, int l, int r) {
    sk[++top] = dis[x]; pa[top] = make_pair(l, r);
    for (int i = h[x]; i; i = p[i])
        if (v[i] != fa && !vis[v[i]]) {
            dis[v[i]] = dis[x] + w[i];
            depth(v[i], x, l, r);
        }
}
void solve(int x) {
    int l, r, i;
    sk[++top] = 0; pa[top] = make_pair(0, 0);
    vis[x] = 1; l = r = top;
    for (i = h[x]; i; i = p[i])
        if (!vis[v[i]]) {
            dis[v[i]] = w[i];
            depth(v[i], x, l, r);
            r = top;
        }
    for (i = h[x]; i; i = p[i])
        if (!vis[v[i]])
            solve(get_root(v[i], x, sz[v[i]]));
}
int main() {
    int i, a, b, c, m;
    scanf("%d%d", &n, &m);
    FOR(i,2,n) {
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c); add(b, a, c);
    }
    solve(get_root(1, 0, n));
    init_rmq(n);
    FOR(i,1,top) if (pa[i].first)
        q.push(Data(pa[i].first, pa[i].second, i));
    while (m--) {
        Node t = q.top(); q.pop();
        printf("%d\n", sk[t.mx] + sk[t.x]);
        if (t.t < t.r) q.push(Data(t.t + 1, t.r, t.x));
        if (t.t > t.l) q.push(Data(t.l, t.t - 1, t.x));
    }
    return 0;
}

树上的路径

Description

给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a

Output

共M行,如题所述.

Sample Input

5 10
1 2 1
1 3 2
2 4 3
2 5 4

Sample Output

7
7
6
5
4
4
3
3
2
1

HINT

N<=50000,M<=Min(300000,n*(n-1) /2 )

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值