Luogu P1552 [APIO2012]派遣 主席树

题目链接 Click Here

这个题好像大多数人用的都是左偏树啊?这里我来贡献一发主席树的解法。

把题目中的问题抽象出来,其实就是询问每一个点的子树中,工资前\(tot_i\)大的点,使它们的和满足\(\sum cost_i<=m\),在此前提下使\(tot_i\)尽可能大,答案就是\(ans=max(tot_i*lead_i)\)

如果只有一个点的话直接二分一下就好了,但现在树上的每一个点都可能是答案的产生1处。为了便于访问每一棵子树,我们把原先的树按\(dfs\)序划分,子树显然就是连续的一个序列。紧接着我们按\(dfs\)序对整棵树进行维护,最后在主席树上每个点二分一下\(tot_i\)的大小就好啦~

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 100010;
int ans;
int n, m, rt, cnt, sz[N], dfn[N], cost[N], lead[N], head[N];

struct edge {
    int nxt, to;

    edge (int _nxt = 0, int _to = 0) {
        nxt = _nxt, to = _to;
    }
}e[N << 1];

void add_len (int u, int v) {
    e[++cnt] = edge (head[u], v); head[u] = cnt;
    e[++cnt] = edge (head[v], u); head[v] = cnt;
}

#define mid ((l + r) >> 1)

int tot;

struct Segment_Tree {
    struct Segment_Node {
        int ls, rs, sz, sum;
        Segment_Node () {sum = ls = rs = sz = 0;}
    }t[N << 5];
    
    int modify (int _rt, int l, int r, int w) {
        int p = ++tot;
        t[p].ls = t[_rt].ls;
        t[p].rs = t[_rt].rs;
        t[p].sz = t[_rt].sz + 1;
        t[p].sum = t[_rt].sum + w;
        if (l != r) {
            if (w <= mid) {
                t[p].ls = modify (t[_rt].ls, l, mid, w);
            } else {
                t[p].rs = modify (t[_rt].rs, mid + 1, r, w);
            }
        }
        return p;
    }

    #undef mid

    int query (int u, int v, int l, int r, int m) {
        int ans = 0;
        while (l < r) {
            int mid = (l + r) >> 1;
            int suml = t[t[v].ls].sum - t[t[u].ls].sum;
            // printf ("v = %d, t[v].sum = %d, t[v].sz = %d, t[ls].sum = %d, t[ls].sz = %d\n", v, t[v].sum, t[v].sz, t[t[v].ls].sum, t[t[v].ls].sz);
            // printf ("l = %d, r = %d, m = %d, mid = %d, suml = %d, sz = %d\n", l, r, m, mid, suml, t[t[v].ls].sz - t[t[u].ls].sz);
            if (m <= suml) {
                u = t[u].ls;
                v = t[v].ls;
                r = mid;
            } else {
                ans += t[t[v].ls].sz - t[t[u].ls].sz;
                u = t[u].rs;
                v = t[v].rs;
                l = mid + 1;
                m -= suml;
            }
        }
        // printf ("m = %d, r = %d, ans = %d\n", m, r, ans);
        ans += floor ((1.0 * m) / (1.0 * r));
        // printf ("ans = %d\n", ans);
        return ans;
    }
}tree;

int root[N];
void pre (int u, int fa) {
    sz[u] = 1;
    dfn[u] = ++dfn[0];
    root[dfn[u]] = tree.modify (root[dfn[u] - 1], 1, m, cost[u]);
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v != fa) {
            pre (v, u);
            sz[u] += sz[v];
        }
    }
}

signed main () {
    cin >> n >> m;
    for (int u = 1; u <= n; ++u) {
        static int v;
        cin >> v >> cost[u] >> lead[u];
        if (v == 0) {
            rt = u;
        } else {
            add_len (u, v);
        }
    }
    pre (rt, 0);
    for (int u = 1; u <= n; ++u) {
        int dfnu = dfn[u];
        int dfnv = dfn[u] + sz[u] - 1;
        ans = max (ans, 1LL * lead[u] * tree.query (root[dfnu - 1], root[dfnv], 1, m, m));
    }
    cout << ans << endl;
} 

posted @ 2019-03-13 14:44  maomao9173  阅读(195)  评论(1编辑  收藏  举报