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;
}

原文地址:https://www.cnblogs.com/maomao9173/p/10523010.html

时间: 2024-08-30 10:22:45

Luogu P1552 [APIO2012]派遣 主席树的相关文章

[luogu P1552] [APIO2012]派遣

[luogu P1552] [APIO2012]派遣 题目背景 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿. 题目描述 在这个帮派里,有一名忍者被称之为Master.除了Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送. 现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为了发送指

BZOJ 2809: [Apio2012]dispatching [主席树 DFS序]

传送门 题意:查询树上根节点值*子树中权值和$\le m$的最大数量 最大值是多少 求$DFS$序,然后变成区间中和$\le m$最多有几个元素,建主席树,然后权值线段树上二分就行了 $WA$:又把边表开小了..... 好吧我$zz$了有根树加无向边干什么.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #de

P1552 [APIO2012]派遣

以m的总价在线段树中找能雇佣的最大人数,然后向上合并 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include"vector" 5 #define cint const int 6 #define newnode() ++idx 7 #include"iostream" 8 using namespace std; 9 int lch[30000

[APIO2012]派遣

P1552 [APIO2012]派遣https://www.luogu.org/problemnew/show/P1552 题目背景 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿. 题目描述 在这个帮派里,有一名忍者被称之为\(Master\).除了\(Master\)以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送. 现在你要招募一批忍者,并把它们派遣给顾客.你需要为

[Luogu 3701] 「伪模板」主席树

[Luogu 3701] 「伪模板」主席树 <题目链接> 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 byx 的树中的每一个人连有向边,手气君的树中的每一个人向 T 连有向边,边权为这个人的寿命.统计同一棵树中的膜法师数量 x.如果一个人是主席,那么边权要加上 x.(续得好啊) 然后,如果 byx 树中的一个点 i 能赢手气君树中的点 j,那么连 i->j,边权为 1. 跑最大

【Luogu】P3384主席树模板(主席树查询K小数)

YEAH!我也是一个AC主席树模板的人了! 其实是个半吊子 我将尽量详细的讲出我的想法. 主席树太难,我们先搞普通线段树好了 普通线段树怎么做?我的想法是查询K次最小值,每次查完把查的数改成INF,查完再改回来... MDZZ 于是就有了主席树. 先不考虑主席树,我们来考虑一个奇特的线段树. 一般的线段树,数列位置是下标,而把数列维护值作为线段树中存的元素. 那我们如果反过来,把数列元素当做线段树的下标...??? 比如说数列[4 2 3 1] 如果线段树的下标是1.2.3.4......? 那

bzoj2809 [ APIO2012 ] -- 主席树

先求出dfs序,然后枚举管理者. 由于只要求数量最多,所以薪水一定从小到大取,用主席树维护,每次在主席树上二分就可以了. 具体看代码. 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 #define N 100010 8 #define

Luogu Dynamic Ranking (带修改的主席树)

带修改的主席树: 原本的主席树是维护了一个线段树前缀. 那么前缀有没有想到什么东西? 树状数组\(Bits\)是不是很 ...... ? 那么现在,我们用树状数组套主席树,不就可以实现带修改的可持久化了吗. 具体来说 \(T[1]维护rt[1]\) , \(T[2]维护rt[1].rt[2]\) , \(T[3]维护rt[3]\) ...... 就与树状数组是一样的. 那么现在,两个具体的操作: 修改: 修改需要修改\(logN\)棵主席树,将涉及修改节点的\(log\)个主席树先删后加点即可.

luogu P2137 Gty的妹子树(分块,主席树)

询问的化我们可以建主席树.然后修改?,树套树...,最后插入?炸了. 所以我们对操作进行分块. 我们先对整棵树建一个主席树.修改,插入我们先记录下来.然后询问的时候先对主席树查询,然后暴力遍历我们记录下来的修改插入操作.每\(\sqrt{m}\)次操作后我们重新构建一个主席树.这样我们保证了重建主席树和询问的总复杂度为\(O(nlogn\sqrt{m})\)然后就把这道题解决了. 有一个难办的事就是如何记录修改和插入的操作.可以使每次询问的时候我们可以知道修改和插入是否在\(u\)的子树中以便我