P4556 [Vani有约会] 雨天的尾巴
虽说是模板题,但是调代码花的时间不少,记录下。
这题的重要思想就是树上差分。例如此题就是在 \(x\) 和 \(y\) 处打上 \((z,1)\) 的标记,在 \(lca(x,y)\) 和 \(fa(lca(x,y))\) 处打上 \((z,-1)\) 的标记。
对于一个点 \(x\) 的实际存放的救济粮就是为 \(x\) 子树内所有的点(包括 \(x\))的值域之和。这里就需要用到线段树合并了,从叶子节点向根节点合并,并计算答案。
当然值域那么大(1e5^2)就得用到动态开点了。
时间复杂度:\(O((n+m)\log n)\)。
本人太弱了,空间是随便开的,不想算
核心代码:
void pushup(int p) {
if (t[ls].mx >= t[rs].mx) t[p].mx = t[ls].mx, t[p].res = t[ls].res;
else t[p].mx = t[rs].mx, t[p].res = t[rs].res;
}
void modify(int &p, int l, int r, int k, int x) {
if (!p) p = ++cnt;
if (l == r) {
t[p].mx += x, t[p].res = l;
if (!t[p].mx) t[p].res = 0;
return;
}
int mid = l+r>>1;
if (k <= mid) modify(ls, l, mid, k, x);
else modify(rs, mid+1, r, k, x);
pushup(p);
}
int merge(int p, int q, int l, int r) {
if (!p || !q) return p+q;
if (l == r) {
t[p].mx += t[q].mx, t[p].res = l;
if (!t[p].mx) t[p].res = 0;
return p;
}
int mid = l+r>>1;
t[p].l = merge(t[p].l, t[q].l, l, mid);
t[p].r = merge(t[p].r, t[q].r, mid+1, r);
pushup(p);
return p;
}
void dfs2(int x, int fa) {
for (int i = head[x]; i; i = e[i].next) if (e[i].to != fa) {
dfs2(e[i].to, x);
rt[x] = merge(rt[x], rt[e[i].to], 1, 1e5);
}
ans[x] = t[rt[x]].res;
}