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