题解:QOJ8283 Game of Votes
设 \(u\) 被淘汰时的票数为 \(r_u\)。淘汰一个人只会使父亲的票数减少,因此淘汰的顺序就是所有点按 \((r_u,u)\) 从大到小排序。
对于叶子节点,\(r_u=a_u\)。考虑如何由儿子节点的 \(r_v\) 推出 \(r_u\)。不妨将儿子节点按 \((r_v,v)\) 从小到大排序得到 \(v_1,\cdots,v_k\)。显然 \(u\) 淘汰时剩余的儿子是一段前缀 \(v_{1\sim i}\)。设 \(pre_i=\sum_{j=1}^ib_{v_j}\),由于保证了 \(u<v_i\),\(i\) 合法的条件为 \(a_u+pre_i>r_i\)。可以想到我们要取的是最大的合法的 \(i\),并令 \(r_u=a_u+pre_i\)。这样我们就能 \(\mathcal{O}(n)\) 解决静态问题。
由于要支持修改,尝试刻画成更好维护的形式。考虑对每个点 \(u\) 开一棵以 \((r_v,v)\) 为关键字的平衡树,我们在节点上维护子树内的 \(b_v\) 之和与 \(\min\{r_v-pre_v\}\),这里 \(pre_v\) 是只考虑子树内点的前缀和。合并是容易的,查询时在平衡树上二分即可。
对于树上的带修问题,套路地考虑把重儿子分离出来考虑。设 \(G_u\) 为只考虑轻儿子时 \(r_u\) 的值,\(H_u\) 为强制加入重儿子时 \(r_u\) 的值。那么 \(r_u\) 的值可以写成关于 \(r_{hson_u}\) 的分段函数:
只需求出 \(u\) 到所在重链链底的函数复合即可查询 \(r_u\),容易使用线段树在 DFS 序上维护区间函数复合。
修改时一直跳重链链顶,求出链顶的 \(r_u\) 后在父亲的平衡树上修改并维护信息即可。
时间复杂度为 \(\mathcal{O}((n+q)\log^2{n})\)。
主要代码
int n, q, fa[MAXN], a[MAXN], b[MAXN], rt[MAXN];
int stmp, dfn[MAXN], rdfn[MAXN];
int dep[MAXN], sz[MAXN], hson[MAXN], top[MAXN], bot[MAXN];
vector<int> T[MAXN];
ll r[MAXN], G[MAXN], H[MAXN];
mt19937_64 gen(random_device{}());
struct Treap {
#define ls(p) (nd[p].ls)
#define rs(p) (nd[p].rs)
int tot, top, stk[MAXN];
struct Node {
int ls, rs;
ll w, sum, mn;
pair<ll, int> val;
ull sd;
} nd[MAXN];
void pushUp(int p) {
nd[p].sum = nd[ls(p)].sum + nd[p].w + nd[rs(p)].sum;
nd[p].mn = min({
nd[ls(p)].mn,
nd[p].val.first - nd[ls(p)].sum - nd[p].w,
nd[rs(p)].mn - nd[ls(p)].sum - nd[p].w
});
}
int create(ll w, pair<ll, int> v) {
int p = top ? stk[top--] : ++tot;
nd[p] = {0, 0, w, w, v.first - w, v, gen()};
return p;
}
void split(int p, pair<ll, int> v, int &x, int &y) {
if (!p) {
x = y = 0;
return;
}
if (nd[p].val <= v) split(rs(p), v, rs(x = p), y);
else split(ls(p), v, x, ls(y = p));
pushUp(p);
}
int merge(int x, int y) {
if (!x || !y) return x | y;
if (nd[x].sd > nd[y].sd) {
rs(x) = merge(rs(x), y);
pushUp(x);
return x;
} else {
ls(y) = merge(x, ls(y));
pushUp(y);
return y;
}
}
void ins(int &p, int x) {
auto v = nd[x].val;
if (!p || nd[p].sd < nd[x].sd) {
split(p, v, ls(x), rs(x));
pushUp(p = x);
return;
}
ins(v >= nd[p].val ? rs(p) : ls(p), x);
pushUp(p);
}
void del(int &p, pair<ll, int> v) {
if (nd[p].val == v) {
stk[++top] = p;
p = merge(ls(p), rs(p));
return;
}
del(v >= nd[p].val ? rs(p) : ls(p), v);
pushUp(p);
}
ll find(int p, ll v, ll pre) {
if (nd[p].mn - pre >= v) return 0;
if (rs(p) && nd[rs(p)].mn - pre - nd[ls(p)].sum - nd[p].w < v) return find(rs(p), v, pre + nd[ls(p)].sum + nd[p].w);
else if (nd[p].val.first - pre - nd[ls(p)].sum - nd[p].w < v) return pre + nd[ls(p)].sum + nd[p].w;
else return find(ls(p), v, pre);
}
#undef ls
#undef rs
} tr;
struct SegTree {
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
struct Func {
ll c1, c2, lim;
ll calc(ll x) const {
return x < lim ? c1 : c2;
}
friend Func operator+(const Func &lhs, const Func &rhs) {
return {lhs.calc(rhs.c1), lhs.calc(rhs.c2), rhs.lim};
}
} nd[MAXN << 2];
void pushUp(int p) {
nd[p] = nd[ls(p)] + nd[rs(p)];
}
void upd(int p, int l, int r, int x, Func v) {
if (l == r) {
nd[p] = v;
return;
}
int mid = l + r >> 1;
if (x <= mid) upd(ls(p), l, mid, x, v);
else upd(rs(p), mid + 1, r, x, v);
pushUp(p);
}
Func query(int p, int l, int r, int x, int y) {
if (x <= l && y >= r) return nd[p];
int mid = l + r >> 1;
if (x <= mid && y > mid) return query(ls(p), l, mid, x, y) + query(rs(p), mid + 1, r, x, y);
else if (x <= mid) return query(ls(p), l, mid, x, y);
else return query(rs(p), mid + 1, r, x, y);
}
#undef ls
#undef rs
} sgt;
void dfs1(int u) {
sz[u] = 1;
for (int v : T[u]) {
dep[v] = dep[u] + 1;
dfs1(v);
sz[u] += sz[v];
if (sz[v] > sz[hson[u]]) hson[u] = v;
}
}
void proc(int u) {
if (hson[u]) {
G[u] = a[u] + tr.find(rt[u], a[u], 0);
H[u] = a[u] + b[hson[u]] + tr.find(rt[u], a[u] + b[hson[u]], 0);
} else {
G[u] = H[u] = a[u];
}
sgt.upd(1, 1, n, dfn[u], {H[u], G[u], H[u]});
}
void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++stmp;
rdfn[stmp] = u;
if (!hson[u]) {
bot[u] = u;
proc(u);
r[u] = a[u];
return;
}
dfs2(hson[u], tp);
bot[u] = bot[hson[u]];
for (int v : T[u]) {
if (v == hson[u]) continue;
dfs2(v, v);
tr.ins(rt[u], tr.create(b[v], {r[v], v}));
}
proc(u);
r[u] = r[hson[u]] < H[u] ? H[u] : G[u];
}
void upd(int u) {
while (top[u] != 1) {
u = top[u];
tr.del(rt[fa[u]], {r[u], u});
r[u] = sgt.query(1, 1, n, dfn[u], dfn[bot[u]]).calc(r[hson[u]]);
tr.ins(rt[fa[u]], tr.create(b[u], {r[u], u}));
proc(u = fa[u]);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 2; i <= n; ++i) {
cin >> fa[i];
T[fa[i]].emplace_back(i);
}
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> b[i];
tr.nd[0].mn = inf;
dfs1(1);
dfs2(1, 1);
while (q--) {
int op;
cin >> op;
if (op == 1) {
int u, x, y;
cin >> u >> x >> y;
a[u] = x;
proc(u);
upd(u);
b[u] = y;
if (fa[u]) {
if (hson[fa[u]] != u) {
tr.del(rt[fa[u]], {r[u], u});
tr.ins(rt[fa[u]], tr.create(b[u], {r[u], u}));
}
proc(fa[u]);
upd(fa[u]);
}
} else {
int x, y;
cin >> x >> y;
r[x] = sgt.query(1, 1, n, dfn[x], dfn[bot[x]]).calc(r[hson[x]]);
r[y] = sgt.query(1, 1, n, dfn[y], dfn[bot[y]]).calc(r[hson[y]]);
cout << (r[x] < r[y] || (r[x] == r[y] && x < y)) << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号