xgc_woker的博客

“人的生命的价值不在于长短,而在于对社会的贡献。”

[Apio2012]dispatching 主席树+dfs序 或 左偏树

Description
给你一棵树,你可以选择一个节点,一个节点有两个值ai、bi,你可以选择它子树里ai总和不超过M的k个点,那么这个节点的价值bi*k,求整棵树的最大价值。


Sample Input
5 4
0 3 3
1 3 5
2 2 2
1 2 4
2 3 1


Sample Output
6


好像不难吧。。。
这道题考虑用主席树做,我们首先有一个贪心的思想,对于一颗子树你肯定是先取ai最小的点,然后再次,再次,那就是求最多能取排名前k个数使它的值小于M,这个东西考虑用主席树维护,然后dfs序一下,维护最大值即可。


#include <cstdio>
#include <cstring>

using namespace std;
typedef long long LL;
LL _max(LL x, LL y) {return x > y ? x : y;}
LL _min(LL x, LL y) {return x < y ? x : y;}

struct edge {
    int x, y, next;
} e[210000]; int len, last[110000];
struct node {
    int lc, rc;
    LL c, sum;
} t[30 * 110000]; int cnt, rt[110000];
int id, ll[110000], rr[110000];
int a[110000], c[110000];

void ins(int x, int y) {
    e[++len].x = x; e[len].y = y;
    e[len].next = last[x]; last[x] = len;
}

void Link(int &u, int l, int r, int p, int c) {
    if(!u) u = ++cnt;
    t[u].c++; t[u].sum += c;
    if(l == r) return ;
    int mid = (l + r) / 2;
    if(p <= mid) Link(t[u].lc, l, mid, p, c);
    else Link(t[u].rc, mid + 1, r, p, c);
}

void Merge(int &u1, int u2) {
    if(!u1 || !u2) {u1 = u1 + u2; return ;}
    t[u1].c += t[u2].c; t[u1].sum += t[u2].sum;
    Merge(t[u1].lc, t[u2].lc);
    Merge(t[u1].rc, t[u2].rc);
}

LL query(int u1, int u2, int l, int r, LL s) {
    if(l == r) return _min(t[u2].c - t[u1].c, s / l);
    LL c = t[t[u2].lc].sum - t[t[u1].lc].sum;
    int k = t[t[u2].lc].c - t[t[u1].lc].c;
    int mid = (l + r) / 2;
    if(c > s) return query(t[u1].lc, t[u2].lc, l, mid, s);
    else return k + query(t[u1].rc, t[u2].rc, mid + 1, r, s - c);
}

void dfs(int x, int fa) {
    ll[x] = ++id;
    for(int k = last[x]; k; k = e[k].next) {
        int y = e[k].y;
        if(fa != y) dfs(y, x);
    }
    rr[x] = id;
}

int main() {
    int n, m; scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        int x; scanf("%d%d%d", &x, &c[i], &a[i]);
        ins(x, i); ins(i, x);
    } dfs(1, 0);
    for(int i = 1; i <= n; i++) {
        int x = ll[i];
        Link(rt[x], 1, 1000000000, c[i], c[i]);
    }
    for(int i = 1; i <= n; i++) Merge(rt[i], rt[i - 1]);
    LL ans = 0;
    for(int i = 1; i <= n; i++) {
        LL hh = query(rt[ll[i] - 1], rt[rr[i]], 1, 1000000000, m);
        ans = _max(ans, hh * a[i]);
    }
    printf("%lld\n", ans);
    return 0;
}

来复习左偏树啦。
我们考虑维护一个大根堆,如果当前sum>M,就一直删除就好了。
左偏树模版打错,调了一早上。。。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
LL _max(LL x, LL y) {return x > y ? x : y;}

struct edge {
    int x, y, next;
} e[110000]; int len, last[110000];
struct node {
    int lc, rc, c, h;
} t[110000]; int cnt;
int a[110000], b[110000], rt[110000], tot[110000], M;
LL sum[110000], ans;

void ins(int x, int y) {
    e[++len].x = x; e[len].y = y;
    e[len].next = last[x]; last[x] = len;
}

int Merge(int u1, int u2) {
    if(!u1 || !u2) return u1 + u2;
    if(a[u1] < a[u2]) swap(u1, u2);
    t[u1].rc = Merge(t[u1].rc, u2);
    if(t[t[u1].lc].h < t[t[u1].rc].h) swap(t[u1].lc, t[u1].rc);
    t[u1].h = t[t[u1].rc].h + 1;
    return u1;
}

void dfs(int x) {
    sum[x] = a[x]; tot[x] = 1;
    for(int k = last[x]; k; k = e[k].next) {
        int y = e[k].y;
        dfs(y);
        rt[x] = Merge(rt[x], rt[y]);
        sum[x] += sum[y]; tot[x] += tot[y];
        while(sum[x] > M && tot[x]) {
            tot[x]--;
            sum[x] -= a[rt[x]];
            rt[x] = Merge(t[rt[x]].lc, t[rt[x]].rc);
        }
    }
    ans = _max(ans, (LL)tot[x] * b[x]);
}

int main() {
    int n; scanf("%d%d", &n, &M);
    for(int i = 1; i <= n; i++) {
        int x; scanf("%d%d%d", &x, &a[i], &b[i]);
        ins(x, i);
    }
    for(int i = 1; i <= n; i++) rt[i] = i;
    dfs(1);
    printf("%lld\n", ans);
    return 0;
}
阅读更多
版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/79980194
上一篇[Tjoi2013]最长上升子序列 树状数组+二分
下一篇BZOJ3439:Kpm的MC密码 字典树+dfs序+主席树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭