P4175 [CTSC2008]网络管理 (动态树上第k大)

题目链接: 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;
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值