HNOI2016網路(整體二分+樹狀陣列)

WAautomaton發表於2019-02-22

題目連結

題目大意

給定一棵樹,每次有三種事件。
1.設定一條路徑uvu\rightarrow v的權值為aa
2.刪除某條設定。
3.詢問所有存在的設定中,不經過xx路徑的權值最大值是多少。
n105,m2×105n\le 10^5,m\le 2\times 10^5

題解

剛開始只想到一個O(nlog3n)O(nlog^3n)的樹剖做法……自己太菜了……
樹剖的話,考慮把一條路徑分成若干段重鏈,在dfs序上形成O(logn)O(logn)個連續區間,那麼它們之間的間隔區間也是O(logn)O(logn)的。
於是我們需要一個資料結構,支援區間插入值,區間刪除值,查詢區間最大值。於是樹套樹就行,或者線段樹每個節點維護一個雙堆,打標記下傳即可。複雜度O(nlog3n)O(nlog^3n)

整體二分真是又好寫複雜度又低的做法……
令二分的值為midmid,考慮當前詢問是否使當前所有>mid>mid的路徑都經過它,如果是,那麼把它扔到左邊;否則扔到右邊。
具體的,我們可以用樹狀陣列維護一個樹上差分,就可以在O(nlog2n)O(nlog^2n)的時間內解決了。

#include <bits/stdc++.h>
namespace IOStream {
	const int MAXR = 10000000;
	char _READ_[MAXR], _PRINT_[MAXR];
	int _READ_POS_, _PRINT_POS_, _READ_LEN_;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
		char c = _READ_[_READ_POS_++];
		if (_READ_POS_ == MAXR) _READ_POS_ = 0;
		if (_READ_POS_ > _READ_LEN_) return 0;
		return c;
	}
	template<typename T> inline void read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-');
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;
	}
	template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
		read(a), read(x...);
	}
	inline int reads(char *s) {
		register int len = 0, c;
		while (isspace(c = readc()) || !c);
		s[len++] = c;
		while (!isspace(c = readc()) && c) s[len++] = c;
		s[len] = 0;
		return len;
	}
	inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
	inline void printc(char c) {
		_PRINT_[_PRINT_POS_++] = c;
		if (_PRINT_POS_ == MAXR) ioflush();
	}
	inline void prints(const char *s) {
		for (int i = 0; s[i]; i++) printc(s[i]);
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		printc(c);
	}
	template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
		print(x, ' '), print(y...);
	}
}
using namespace IOStream;
using namespace std;
typedef long long ll;
typedef pair<int, int> P;

const int MAXN = 200005;
struct Edge { int to, next; } edge[MAXN];
struct Option { int t, tp, u, v, x, l; } opt[MAXN], lo[MAXN], ro[MAXN];
struct Query { int t, x, id; } ask[MAXN], lq[MAXN], rq[MAXN];
int head[MAXN], beg[MAXN], ed[MAXN], st[20][MAXN], dep[MAXN];
int lg[MAXN], bit[MAXN], srt[MAXN], par[MAXN], res[MAXN], n, m, tot;
void addedge(int u, int v) {
	edge[++tot] = (Edge) { v, head[u] };
	head[u] = tot;
}
void dfs(int u, int fa) {
	st[0][beg[u] = ++tot] = u;
	par[u] = fa, dep[u] = dep[fa] + 1;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (v == fa) continue;
		dfs(v, u);
		st[0][++tot] = u;
	}
	ed[u] = tot;
}
void dfs2(int u, int fa) {
	beg[u] = ++tot;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		if (v != fa) dfs2(v, u);
	}
	ed[u] = tot;
}
int get_min(int x, int y) { return dep[x] < dep[y] ? x : y; }
int get_lca(int x, int y) {
	x = beg[x], y = beg[y];
	if (x > y) swap(x, y);
	int l = lg[y - x + 1];
	return get_min(st[l][x], st[l][y - (1 << l) + 1]);
}
void add(int x, int w) { x = beg[x]; for (; x <= n; x += x & -x) bit[x] += w; }
void clear(int x) { x = beg[x]; for (; x <= n; x += x & -x) bit[x] = 0; }
int sum(int x) { int res = 0; for (; x; x -= x & -x) res += bit[x]; return res; }
void divide(int l, int r, int ql, int qr, int cl, int cr) {
	if (ql > qr) return;
	if (l == r) {
		for (int i = ql; i <= qr; i++) res[ask[i].id] = srt[l];
		return;
	}
	int mid = (l + r) >> 1, clo = 0, cro = 0, clq = 0, crq = 0, now = 0;
	for (int i = cl; i <= cr; i++) {
		if (opt[i].x > srt[mid]) ro[++cro] = opt[i];
		else lo[++clo] = opt[i];
	}
	if (cro == 0) divide(l, mid, ql, qr, cl, cr);
	for (int i = 1, j = ql; i <= cro; i++) {
		if (ro[i].tp == 0) {
			++now;
			add(ro[i].u, 1), add(ro[i].v, 1), add(ro[i].l, -1);
			if (par[ro[i].l]) add(par[ro[i].l], -1);
		} else {
			--now;
			add(ro[i].u, -1), add(ro[i].v, -1), add(ro[i].l, 1);
			if (par[ro[i].l]) add(par[ro[i].l], 1);
		}
		for (; j <= qr && ask[j].t < ro[i].t; ++j) lq[++clq] = ask[j];
		for (; j <= qr && (i == cro || ask[j].t < ro[i + 1].t); ++j) {
			if (sum(ed[ask[j].x]) - sum(beg[ask[j].x] - 1) == now) lq[++clq] = ask[j];
			else rq[++crq] = ask[j];
		}
	}
	for (int i = 1; i <= clo; i++) opt[i + cl - 1] = lo[i];
	for (int i = 1; i <= cro; i++) {
		clear(ro[i].u), clear(ro[i].v), clear(ro[i].l);
		if (par[ro[i].l]) clear(par[ro[i].l]);
		opt[i + clo + cl - 1] = ro[i];
	}
	for (int i = 1; i <= clq; i++) ask[i + ql - 1] = lq[i];
	for (int i = 1; i <= crq; i++) ask[i + clq + ql - 1] = rq[i];
	divide(l, mid, ql, ql + clq - 1, cl, cl + clo - 1);
	divide(mid + 1, r, ql + clq, qr, cl + clo, cr);
}
int main() {
	read(n, m);
	for (int i = 1; i < n; i++) {
		int u, v; read(u, v);
		addedge(u, v), addedge(v, u);
	}
	dfs(1, tot = 0);
	for (int i = 1; i < 20; i++)
	for (int j = 1; j + (1 << i) - 1 <= tot; j++)
		st[i][j] = get_min(st[i - 1][j], st[i - 1][j + (1 << i >> 1)]);
	for (int i = 2; i <= tot; i++) lg[i] = lg[i >> 1] + 1;
	int cnt = 0, qc = 0, oc = 0;
	for (int i = 1; i <= m; i++) {
		int o; read(o);
		if (o == 2) read(ask[++qc].x), ask[qc].t = i, ask[qc].id = qc;
		else if (o == 0) {
			++oc, read(opt[oc].u, opt[oc].v, opt[oc].x);
			opt[oc].tp = 0, opt[oc].t = i, srt[++cnt] = opt[oc].x;
			opt[oc].l = get_lca(opt[oc].u, opt[oc].v);
			lo[i] = opt[oc];
		} else {
			++oc, read(opt[oc].x), opt[oc] = lo[opt[oc].x];
			opt[oc].tp = 1, opt[oc].t = i;
		}
	}
	dfs2(1, tot = 0);
	srt[0] = -1;
	sort(srt + 1, srt + 1 + cnt);
	cnt = unique(srt + 1, srt + 1 + cnt) - srt - 1;
	divide(0, cnt, 1, qc, 1, oc);
	for (int i = 1; i <= qc; i++) print(res[i]);
	ioflush();
	return 0;
}

相關文章