bzoj4538: [HNOI2016]网络

题目链接

维护一颗树。在每个时刻,有以下三个操作。

  1. ( u , v , w ) (u,v,w) (u,v,w): 向 u u u v v v的路径上添加一个重要度为 w w w的任务。
  2. ( t ) (t) (t): 取消 t t t时刻添加的任务。
  3. ( x ) (x) (x) 查询所有未覆盖 x x x节点的任务中重要度的最大值。

这道题有诸多做法,然而我直接当做树链剖分练习题来做了。

对于这样的三个操作,我们可以考虑用树剖来维护。查询不覆盖某节点的任务难以直接用线段树实现,我们可以反其道而行之,将操作 1 1 1中的覆盖区间取补集,查询时输出 x x x点被 1 1 1操作覆盖的任务的最大值。这样的操作与原操作等价。考虑到一个节点会被多个任务覆盖或取消覆盖,可以向线段树的每个节点套一个大根堆,查询时对所有覆盖该点的线段的堆顶元素取 m a x max max。三个树套在一起,时间复杂度为 O ( N l o g 3 N ) O(Nlog^3N) O(Nlog3N),空间复杂度为 O ( N l o g 2 N ) O(Nlog^2N) O(Nlog2N)

这题还可以用kdtree,点分治,二分+树链交等各种优雅的做法秒杀,然而我一个都不会。

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

struct event {
	int val, id;
	inline bool operator < (const event &rhs) const {
		return val < rhs.val;
	}
}tmpe;

typedef priority_queue<event> heap;
const int N = 100010;

int n, m;
vector<int> E[N];

int size[N], dep[N], fa[N], son[N], top[N], seg[N], segCnt;

void dfs1(int u, int f) {
	size[u] = 1; dep[u] = dep[fa[u] = f] + 1;
	for (int i = 0; i < E[u].size(); i++) {
		int &v = E[u][i];
		if (v == f) continue;
		dfs1(v, u);
		size[u] += size[v];
		if (size[v] > size[son[u]])
			son[u] = v;
	}
}

void dfs2(int u, int tp) {
	top[u] = tp; seg[u] = ++segCnt;
	if (! son[u]) return;
	dfs2(son[u], tp);
	for (int i = 0; i < E[u].size(); i++) {
		int &v = E[u][i];
		if (v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}

inline void initTree() {
	dfs1(1, 0); dfs2(1, 1);
}

bool disable[N<<1];
heap hp[N<<1];
#define g(l, r) (l + r | l != r)
#define o g(l, r)
#define ls g(l, mid)
#define rs g(mid + 1, r)
int gl, gr, gans;
#define cg(l, r) gl = l; \
		gr = r
#define H hp[o]

void ins(int l, int r) {
	if (gl > gr) return;
	if (gl <= l && r <= gr) {
		H.push(tmpe); return;
	}
	int mid = l + r >> 1;
	if (gl <= mid) ins(l, mid);
	if (mid + 1 <= gr) ins(mid + 1, r);
}

void query(int l, int r) {
	while (! H.empty() && disable[H.top().id]) H.pop();
	if (! H.empty()) gans = max(gans, H.top().val);
	if (l == r) return;
	int mid = l + r >> 1;
	if (gl <= mid) query(l, mid);
	else query(mid + 1, r);
}

event cf[110<<1];
inline void Insert(int u, int v) {
	int ecnt = 0;
	int f1 = top[u], f2 = top[v];
	while (f1 != f2) {
		if (dep[f1] < dep[f2]) {
			swap(f1, f2); swap(u, v);
		}
		cf[++ecnt] = (event){seg[f1], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};
		u = fa[f1], f1 = top[u];
	}
	if (dep[u] < dep[v]) swap(u, v);
	cf[++ecnt] = (event){seg[v], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};
	
	//O(logNlog(logN))差分求补集
	sort(cf + 1, cf + 1 + ecnt);
	int sum = 0, left = 1;
	for (int i = 1; i <= ecnt; i++) {
		if (sum == 0) {
			cg(left, cf[i].val - 1); ins(1, n);
			sum += cf[i].id;
		} else {
			sum += cf[i].id;
			if (sum == 0) left = cf[i].val;
		}
	}
	cg(left, n); ins(1, n);
}

inline void Query(int u) {
	gans = -1;
	cg(seg[u], seg[u]); query(1, n);
	printf("%d\n", gans);
}

inline void work() {
	for (int id = 1; id <= m; id++) {
		int type; scanf("%d", &type);
		if (type == 0) {
			int a, b, v; scanf("%d%d%d", &a, &b, &v);
			tmpe = (event){v, id};
			Insert(a, b);
		}
		if (type == 1) {
			int t; scanf("%d", &t);
			disable[t] = true;
		}
		if (type == 2) {
			int x; scanf("%d", &x);
			Query(x);
		}
	}
}

int main() {
	cin >> n >> m;
	for (int i = 1; i < n; i++) {
		int u, v; scanf("%d%d", &u, &v);
		E[u].push_back(v); E[v].push_back(u);
	}
	initTree();
	work();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值