[做题记录-数据结构]Luogu P7889 「MCOI-06」Eert Tuc Knil 题解
题意
给定一颗 \(n\) 个节点有根树,第 \(i\) 节点权值为 \(a_i\)。
在这个树上支持一种询问:
给定节点 \(u\) 和参数 \(x\),假如 所有节点点权加 \(x\),在这种情况下,求: 对于所有完全在 \(u\) 子树内并包含 \(u\) 的连通点集,权值之和最大可能为多少?
\(n \leq 10^6\)
题解
考虑一个\(\text{dp}\), \(f_x = val_x +\sum \max\{f_y, 0\}\)。
可以把查询离线, 然后只剩下加法。然后考虑如何维护加法。
先对原树进行一次\(dp\), 然后可以转移的边视为存在, 否则不存在。每次找到一条不存在的边使得其存在即可维护答案。
一条边不存在当且仅当其连接的连通块权值小于\(0\)。那么把连通块按照\(\frac{-val}{sz}\)排序, 每次取出最小的, 判断是否可以连上, 执行合并即可。
然后现在的问题是如何快速进行合并和维护答案。
维护答案的部分所需要的是维护一个点所连接的子树内的联通块的和以及大小。可以维护一个联通块的最高点, 这样的话就是执行一次链加。
那么现在就是要做链加和单点查, 可以树剖\(\log^2\)。
换成子树查可以做到一个\(\log\)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
template<typename T>
inline void read(T &x) {
x = 0; char a = getchar(); bool f = 0;
for(; ! isdigit(a); a = getchar()) if(a == '-') f = 1;
for(; isdigit(a); a = getchar()) x = x * 10 + a - '0';
if(f) x = -x;
}
#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (r); i >= (l); i --)
const int N = 1e6 + 10;
const ll Lim = 1e13;
int n, m;
struct BIT {
ll c[N];
#define lowbit(x) (x & -x)
void upd(int x, ll v) {
for(; x <= n; x += lowbit(x)) c[x] += v;
}
ll ask(int x) {
ll res = 0;
for(; x; x -= lowbit(x)) res += c[x];
return res;
}
ll qry(int l, int r) {
return ask(r) - ask(l - 1);
}
} c1, c2;
int Fa[N];
inline int find(int x) { return x == Fa[x] ? x : Fa[x] = find(Fa[x]); }
int fa[N];
vector<int> e[N];
struct Qry {
int id, x;
ll v;
} q[N];
ll val[N];
int dfn[N], sz[N];
void dfs(int x) {
dfn[x] = ++ dfn[0];
sz[x] = 1;
for(int y : e[x]) dfs(y), sz[x] += sz[y];
}
void modify(int x, int y, ll v, int tv) {
c1.upd(dfn[x], v);
c2.upd(dfn[x], tv);
y = fa[y];
if(y) {
c1.upd(dfn[y], -v);
c2.upd(dfn[y], -tv);
}
}
struct Node {
ll v; int x;
Node() {}
Node(ll _v, int _x) : v(_v), x(_x) {}
inline bool operator <(const Node &t) const {
return v != t.v ? v > t.v : x < t.x;
}
inline bool operator ==(const Node &t) const {
return v == t.v && x == t.x;
}
} ;
struct Queue {
priority_queue<Node> q1, q2;
void push(Node v) {
q1.push(v);
}
void erase(Node v) {
q2.push(v);
}
Node top() {
while(q1.size() && q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
return q1.top();
}
inline int size() {
return q1.size() - q2.size();
}
} Q;
ll ans[N];
void link(int x) {
ll sum = c1.qry(dfn[x], dfn[x] + sz[x] - 1);
int ssz = c2.qry(dfn[x], dfn[x] + sz[x] - 1);
int t = find(fa[x]);
modify(fa[x], t, sum, ssz);
Fa[x] = t;
if(t != 1) {
Q.push( Node( ceil (1.0 * (- c1.qry(dfn[t], dfn[t] + sz[t] - 1)) / c2.qry(dfn[t], dfn[t] + sz[t] - 1) + 1e-9), t ) );
}
}
int main() {
read(n); read(m);
lep (i, 2, n) read(fa[i]), e[fa[i]].push_back(i);
lep (i, 1, n) read(val[i]);
lep (i, 1, m) {
read(q[i].x); read(q[i].v); q[i].id = i;
}
lep (i, 1, n) val[i] -= Lim;
dfs(1);
lep (i, 1, n) Fa[i] = i, modify(i, i, val[i], 1);
lep (i, 1, m) q[i].v += Lim;
sort(q + 1, q + 1 + m, [] (Qry a, Qry b) { return a.v < b.v; } );
lep (i, 2, n) {
Q.push( Node(- val[i] / 1, i) );
}
lep (i, 1, m) {
while(Q.size() && Q.top().v <= q[i].v) {
Node tmp = Q.top();
int fx = fa[tmp.x];
Q.erase(tmp);
fx = find(fx);
if(fx != 1) {
Q.erase( Node( ceil (1.0 * (- c1.qry(dfn[fx], dfn[fx] + sz[fx] - 1)) / c2.qry(dfn[fx], dfn[fx] + sz[fx] - 1) + 1e-9), fx ) );
}
link(tmp.x);
}
ans[q[i].id] = c1.qry(dfn[q[i].x], dfn[q[i].x] + sz[q[i].x] - 1) + c2.qry(dfn[q[i].x], dfn[q[i].x] + sz[q[i].x] - 1) * q[i].v;
}
lep (i, 1, m) printf("%lld\n", ans[i]);
return 0;
}