题目链接: P4175 [CTSC2008]网络管理
大致题意
给定一棵有 n n n个节点的树, 节点上有权值 w i w_i wi. 有 m m m次操作.
0 a b
表示把节点
a
a
a的权值修改为
b
b
b. 即:
w
a
=
b
w_a = b
wa=b
k a b
查询
(
a
,
b
)
(a, b)
(a,b)最短路径上, 第
k
k
k大的值.
解题思路
➡️静态树上第 k k k小⬅️点这里
如果没有做过这个题的静态版本, ⬆️推荐博客⬆️. 下文讲解也会基于上述博客.
很多静态树上第 k k k小的做法也都适用于本题, 可以参考上述博客, 本文只提几个复杂度比较优秀的做法
思路一: 动态主席树 + 树上差分 复杂度: O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)
同静态做法思路, 只不过动态版本带修改了, 我们把静态树换成动态树去做即可.
思路二: 整体二分 复杂度: O ( m l o g 3 n ) O(mlog^3n) O(mlog3n) / O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)
同静态做法思路, 实测 l o g 3 n log^3n log3n的做法跑的更快一些, 于是下文代码只放 l o g 3 n log^3n log3n的做法.
AC代码
动态主席树 + 树上差分
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 8E4 + 10;
int n, m;
int w[N];
vector<int> edge[N];
/* 离散化模版 */
vector<int> v(1, -0x3f3f3f3f); // int len;
int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }
void discrete() { sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); }
/* 动态主席树模版 */
const int L = 0; int R = N - 5;
int rt[N], root[N]; //root是动态树, rt是静态树
struct CRT {
struct node {
int l, r;
int val;
}t[N * 18 * 18];
int ind; //多组记得清空
int build(int a, int c, int tl, int tr, int p) { // 静态树建立
int x = ++ind; t[x] = t[p];
t[x].val += c;
if (tl == tr) return x;
int mid = tl + tr >> 1;
if (a <= mid) t[x].l = build(a, c, tl, mid, t[p].l);
else t[x].r = build(a, c, mid + 1, tr, t[p].r);
return x;
}
void modify(int a, int c, int tl, int tr, int& x) { //修改动态树
if (!x) x = ++ind;
t[x].val += c;
if (tl == tr) return;
int mid = tl + tr >> 1;
a <= mid ? modify(a, c, tl, mid, t[x].l) : modify(a, c, mid + 1, tr, t[x].r);
}
int ask(int k, int tl, int tr, vector<int>& p, vector<int>& x) {
if (tl == tr) return tl;
int cou = 0;
for (auto& op : x) cou += t[t[op].r].val;
for (auto& op : p) cou -= t[t[op].r].val;
int mid = tl + tr >> 1;
vector<int> np, nx;
if (cou >= k) {
for (auto& op : x) if (t[op].r) nx.push_back(t[op].r);
for (auto& op : p) if (t[op].r) np.push_back(t[op].r);
return ask(k, mid + 1, tr, np, nx);
}
for (auto& op : p) if (t[op].l) np.push_back(t[op].l);
for (auto& op : x) if (t[op].l) nx.push_back(t[op].l);
return ask(k - cou, tl, mid, np, nx);
}
}crt;
/* 树链剖分求lca模板 */
int p[N], dep[N], sz[N], son[N];
void dfs1(int x = 1, int fa = 0) {
p[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1; // son[x] = 0;
for (auto& to : edge[x]) {
if (to == fa) continue;
dfs1(to, x);
sz[x] += sz[to];
if (sz[to] > sz[son[x]]) son[x] = to;
}
}
int id[N], top[N], ind;
void dfs2(int x = 1, int tp = 1) {
id[x] = ++ind, top[x] = tp;
w[x] = find(w[x]);
rt[id[x]] = crt.build(w[x], 1, L, R, rt[id[p[x]]]);
if (!son[x]) return;
dfs2(son[x], tp);
for (auto& to : edge[x]) {
if (to == p[x] or to == son[x]) continue;
dfs2(to, to);
}
}
int lca(int a, int b) {
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
a = p[top[a]];
}
return id[a] < id[b] ? a : b;
}
struct BIT {
static int lowbit(int x) { return x & -x; }
void add(int x, int a, int c) { for (int i = x; i <= n; i += lowbit(i)) crt.modify(a, c, L, R, root[i]); }
void modify(int l, int r, int a, int c) { add(l, a, c), add(r + 1, a, -c); }
void modify_subtree(int a, int c) { modify(id[a], id[a] + sz[a] - 1, w[a], c); }
int ask(int a, int b, int k) {
int lca = ::lca(a, b), plca = p[lca];
vector<int> nx = { rt[id[a]], rt[id[b]] }, np = { rt[id[lca]], rt[id[plca]] };
for (int i = id[a]; i; i -= lowbit(i)) if (root[i]) nx.push_back(root[i]);
for (int i = id[b]; i; i -= lowbit(i)) if (root[i]) nx.push_back(root[i]);
for (int i = id[lca]; i; i -= lowbit(i)) if (root[i]) np.push_back(root[i]);
for (int i = id[plca]; i; i -= lowbit(i)) if (root[i]) np.push_back(root[i]);
return crt.ask(k, L, R, np, nx);
}
}bit;
struct operation {
int tp, l, r, k;
// 0 a c NULL
}; vector<operation> area;
int main()
{
cin >> n >> m;
rep(i, n) scanf("%d", &w[i]), v.push_back(w[i]);
rep(i, n - 1) {
int a, b; scanf("%d %d", &a, &b);
edge[a].push_back(b), edge[b].push_back(a);
}
rep(i, m) {
int k, a, b; scanf("%d %d %d", &k, &a, &b);
if (!k) {
area.push_back({ 0, a, b, NULL });
v.push_back(b);
}
else area.push_back({ 1, a, b, k });
}
discrete(); R = v.size() - 1;
dfs1(), dfs2();
for (auto& [tp, a, b, k] : area) {
if (!tp) {
bit.modify_subtree(a, -1);
w[a] = find(b);
bit.modify_subtree(a, 1);
}
else {
int res = bit.ask(a, b, k);
if (!res) puts("invalid request!");
else printf("%d\n", v[res]);
}
}
return 0;
}
整体二分
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 8E4 + 10;
int n, m;
int w[N];
vector<int> v(1, -0x3f3f3f3f);
vector<int> edge[N];
int p[N], dep[N], sz[N], son[N];
// 父节点 深度 节点大小 重儿子
void dfs1(int x = 1, int fa = 0) { // x = 树根节点
p[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1; // 多组: son[x] = 0; 清ind!!!
for (auto& to : edge[x]) {
if (to == fa) continue;
dfs1(to, x);
sz[x] += sz[to]; // 特别的, 如果边权->点权, 应记录w[to] = 边权.
if (sz[to] > sz[son[x]]) son[x] = to; //更新重儿子
}
}
int id[N], nw[N], top[N], ind; // * 多组记得清ind
// 新编号 新值 重链顶 当前用到的编号
void dfs2(int x = 1, int tp = 1) { // x = 树根节点, tp = 树根节点
id[x] = ++ind, nw[ind] = w[x], top[x] = tp;
if (!son[x]) return; //叶子结点
dfs2(son[x], tp); //先遍历重儿子
for (auto& to : edge[x]) {
if (to == p[x] or to == son[x]) continue;
dfs2(to, to);
}
}
struct BIT {
int t[N];
static int lowbit(int x) { return x & -x; }
void add(int x, int c) { for (int i = x; i <= n; i += lowbit(i)) t[i] += c; }
int ask(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += t[i];
return res;
}
int ask(int l, int r) { return ask(r) - ask(l - 1); }
}bit;
int ask_route(int a, int b) {
int res = 0;
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
res += bit.ask(id[top[a]], id[a]);
a = p[top[a]];
}
if (id[a] > id[b]) swap(a, b);
res += bit.ask(id[a], id[b]);
return res;
}
struct operation {
int tp, a, b, k, id;
// 0 a c flag NULL;
}; vector<operation> area;
int res[N];
void fact(int l, int r, vector<operation>& q) {
if (q.empty()) return;
if (l == r) {
for (auto& [tp, a, b, k, id] : q) if (tp) res[id] = l;
return;
}
int mid = l + r >> 1;
vector<operation> ql, qr;
for (auto& op : q) {
auto& [tp, a, b, k, id] = op;
if (!tp) {
if (b > mid) bit.add(a, k), qr.push_back(op);
else ql.push_back(op);
}
else {
int cou = ask_route(a, b);
if (cou >= k) qr.push_back(op);
else k -= cou, ql.push_back(op);
}
}
for (auto& op : qr) if (!op.tp) bit.add(op.a, -op.k);
fact(l, mid, ql), fact(mid + 1, r, qr);
}
int main()
{
cin >> n >> m;
rep(i, n) scanf("%d", &w[i]), v.push_back(w[i]);
rep(i, n - 1) {
int a, b; scanf("%d %d", &a, &b);
edge[a].push_back(b), edge[b].push_back(a);
}
dfs1(), dfs2();
rep(i, n) area.push_back({ 0, i, nw[i], 1, NULL });
rep(i, m) {
int k, a, b; scanf("%d %d %d", &k, &a, &b);
if (!k) {
res[i] = -1;
area.push_back({ 0, id[a], w[a], -1, NULL });
w[a] = b;
area.push_back({ 0, id[a], w[a], 1, NULL });
v.push_back(w[a]);
}
else area.push_back({ 1, a, b, k, i });
}
sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
for (auto& op : area) if (!op.tp) op.b = lower_bound(v.begin(), v.end(), op.b) - v.begin();
fact(0, v.size() - 1, area);
rep(i, m) {
if (res[i] != -1) {
if (res[i] == 0) puts("invalid request!");
else printf("%d\n", v[res[i]]);
}
}
return 0;
}