[题解] CF487E Tourists

前言

运用算法:点双连通分量,圆方树,树链剖分,splay(可以用 multiset 维护,不知道 OI 可不可以使用,所以就用了 splay )。

题目链接

题目大意

(n) 个点, (m) 条边,每个点 (i) 都有点权 (w_i) 。有两个操作:

  1. C a w: 表示 a 点的权值变为 w。
  2. A a b: 求从 a 点到 b 点的一条路径上,使得每个点只出现一次,且最小权值最小的一条路径的最小值。

思路

先说明圆方树,经常用于求解与无向图。下见如何构造圆方树(来自 WC 的 PPT 的一张图):
在这里插入图片描述

先用 Tarjan 算法求出点双连通分量,原有的点成为圆点。对于每个点双连通分量,建立一个方点,将点双连通分量内的所有圆点都连向这个方点。

显然,有一条重要性质成立:每条边的两个端点,一个是圆点,一个是方点。

对于一个普通的无向图,要进行维护其点权,可以先将其变为圆方树,在进行维护,是一些题的基本思路。树相对于图有更好的性质,可以用树链剖分等来维护。

题目中要维护最小值,那么方点的权值就为所连圆点的最小值。最小值使用 splay 维护,最后按照要求进行维护即可。

修改一个圆点,就会改动其旁边所有的方点。考虑菊花图,会被卡成 (O(nq))

稍微在上面做出改动,每个方点的权值为子节点的权值,暂时不考虑其父节点。树剖查询时,若两点的 LCA 为方点,最小值多比较一个父节点的权值即可。

时间复杂度 (O(qlog^2n+nlog n))

Code

#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
namespace Quick_Function {
	template <typename Temp> void Read(Temp &x) {
		x = 0; char ch = getchar(); bool op = 0;
		while(ch < '0' || ch > '9') { if(ch == '-') op = 1; ch = getchar(); }
		while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
		if(op) x = -x;
	}
	template <typename T, typename... Args> void Read(T &t, Args &... args) { Read(t); Read(args...); }
	template <typename Temp> Temp Max(Temp x, Temp y) { return x > y ? x : y; }
	template <typename Temp> Temp Min(Temp x, Temp y) { return x < y ? x : y; }
	template <typename Temp> Temp Abs(Temp x) { return x < 0 ? (-x) : x; }
	template <typename Temp> void Swap(Temp &x, Temp &y) { x ^= y ^= x ^= y; }
}
using namespace Quick_Function;
#define INF 0x3f3f3f3f
const int MAXN = 1e6 + 5;
struct edge1 { int To, Next; };
edge1 edge1[MAXN << 1], edge2[MAXN << 1];
int head1[MAXN], head2[MAXN];
int edgetot1, edgetot2;
void Addedge1(int u, int v) {
	edge1[++edgetot1].Next = head1[u], edge1[edgetot1].To = v, head1[u] = edgetot1;
	edge1[++edgetot1].Next = head1[v], edge1[edgetot1].To = u, head1[v] = edgetot1;
}
void Addedge2(int u, int v) {
	edge2[++edgetot2].Next = head2[u], edge2[edgetot2].To = v, head2[u] = edgetot2;
	edge2[++edgetot2].Next = head2[v], edge2[edgetot2].To = u, head2[v] = edgetot2;
}
int w[MAXN], n, m, q;
stack<int> stk;
int dfn1[MAXN], low[MAXN];
int tim1, cnt;
void Tarjan(int u) {
	dfn1[u] = low[u] = ++tim1; stk.push(u);
	for(int i = head1[u]; i; i = edge1[i].Next) {
		int v = edge1[i].To;
		if(!dfn1[v]) {
			Tarjan(v);
			low[u] = Min(low[u], low[v]);
			if(low[v] >= dfn1[u]) {
				int Top = -1; cnt++;
				while(Top != v) {
					Top = stk.top(); stk.pop();
					Addedge2(Top, cnt);
				}
				Addedge2(u, cnt);
			}
		}
		else low[u] = Min(low[u], dfn1[v]);
	}
}
struct Segment_Node {
	int Left, Right, MinData;
	#define LS(x) (x << 1)
	#define RS(x) (x << 1 | 1)
	#define L(x) segment_tree[x].Left
	#define R(x) segment_tree[x].Right
	#define M(x) segment_tree[x].MinData
};
struct Segment_Tree {
	Segment_Node segment_tree[MAXN << 2];
	void Push_Up(int pos) { M(pos) = Min(M(LS(pos)), M(RS(pos))); }
	void Build(int pos, int l, int r) {
		L(pos) = l, R(pos) = r;
		if(l == r) return;
		int mid = (l + r) >> 1;
		Build(LS(pos), l, mid), Build(RS(pos), mid + 1, r);
	}
	void Update(int pos, int x, int k) {
		if(L(pos) == R(pos)) { M(pos) = k; return; }
		if(x <= R(LS(pos))) Update(LS(pos), x, k);
		else Update(RS(pos), x, k);
		Push_Up(pos);
	}
	int Query_Min(int pos, int l, int r) {
		if(l <= L(pos) && R(pos) <= r) return M(pos);
		int res = INF;
		if(l <= R(LS(pos))) res = Min(res, Query_Min(LS(pos), l, r));
		if(r >= L(RS(pos))) res = Min(res, Query_Min(RS(pos), l, r));
		return res;
	}
};
Segment_Tree tree1;
struct Splay_Node {
	int Child[2], Val, Cnt, Siz, Father, Belong;
	#define Son(x, y) splay[x].Child[y]
	#define V(x) splay[x].Val
	#define C(x) splay[x].Cnt
	#define S(x) splay[x].Siz
	#define F(x) splay[x].Father
	#define B(x) splay[x].Belong
};
struct Splay_Tree {
	int root[MAXN], tot;
	Splay_Node splay[MAXN * 3];
	bool Ident(int pos) { return Son(F(pos), 1) == pos; }
	int New(int val, int fa, int now) {
		F(++tot) = fa;
		C(tot) = S(tot) = 1;
		V(tot) = val;
		B(tot) = now;
		return tot;
	}
	void Build() {
		for(int i = 1; i <= cnt - n; i++) {
			root[i] = New(-INF, 0, i);
			Son(root[i], 1) = New(INF, root[i], i);
		}
	}
	void Update(int pos) { S(pos) = C(pos) + S(Son(pos, 0)) + S(Son(pos, 1)); }
	void Connect(int pos, int fa, int flag) {
		Son(fa, flag) = pos;
		F(pos) = fa;
	}
	void Rotate(int pos) {
		int fa = F(pos), grand = F(fa);
		int flag1 = Ident(pos), flag2 = Ident(fa);
		Connect(Son(pos, flag1 ^ 1), fa, flag1);
		Connect(fa, pos, flag1 ^ 1);
		Connect(pos, grand, flag2);
		Update(fa);
		Update(pos);
	}
	void Splay(int pos, int to) {
		for(int fa = F(pos); F(pos) != to; Rotate(pos), fa = F(pos))
			if(F(fa) != to) Ident(pos) == Ident(fa) ? Rotate(fa) : Rotate(pos);
		if(!to) root[B(pos)] = pos;
	}
	void Insert(int &pos, int val, int fa) {
		if(!pos) {
			pos = New(val, fa, B(fa));
			Splay(pos, 0);
			return;
		}
		if(val == V(pos)) {
			C(pos)++;
			Splay(pos, 0);
			return;
		}
		else if(val < V(pos)) Insert(Son(pos, 0), val, pos);
		else Insert(Son(pos, 1), val, pos);
	}
	void Remove(int pos, int val) {
		if(!pos) return;
		if(val == V(pos)) {
			if(C(pos) > 1) {
				C(pos)--;
				Splay(pos, 0);
				return;
			}
			if(Son(pos, 0)) Rotate(Son(pos, 0)), Remove(pos, val);
			else if(Son(pos, 1)) Rotate(Son(pos, 1)), Remove(pos, val);
			else {
				int newroot = F(pos);
				Son(F(pos), Ident(pos)) = 0;
				Splay(newroot, 0);
			}
			return;
		}
		else if(val < V(pos)) Remove(Son(pos, 0), val);
		else Remove(Son(pos, 1), val);
	}
	int Query_Min(int pos) {
		if(!pos) return INF;
		if(S(Son(pos, 0)) >= 2) return Query_Min(Son(pos, 0));
		Splay(pos, 0); return V(pos);
	}
};
Splay_Tree tree2;
int dfn2[MAXN], siz[MAXN], dep[MAXN], son[MAXN], fa[MAXN], tp[MAXN];
int tim2;
void dfs1(int u, int pre) {
	dep[u] = dep[pre] + 1, fa[u] = pre, siz[u] = 1;
	int maxn = -INF;
	for(int i = head2[u]; i; i = edge2[i].Next) {
		int v = edge2[i].To;
		if(v == pre) continue;
		dfs1(v, u); siz[u] += siz[v];
		if(siz[v] > maxn) son[u] = v, maxn = siz[v];
	}
}
void dfs2(int u, int Top) {
	dfn2[u] = ++tim2, tp[u] = Top;
	if(son[u]) dfs2(son[u], Top);
	for(int i = head2[u]; i; i = edge2[i].Next) {
		int v = edge2[i].To;
		if(v != son[u] && v != fa[u]) dfs2(v, v);
	}
}
int Min_Past(int x, int y) {
	int res = INF;
	while(tp[x] != tp[y]) {
		if(dep[tp[x]] < dep[tp[y]]) Swap(x, y);
		res = Min(res, tree1.Query_Min(1, dfn2[tp[x]], dfn2[x]));
		x = fa[tp[x]];
	}
	if(dep[x] > dep[y]) Swap(x, y);
	res = Min(res, tree1.Query_Min(1, dfn2[x], dfn2[y]));
	if(x > n) res = Min(res, w[fa[x]]);
	return res;
}
int main() {
	Read(n, m, q); cnt = n;
	for(int i = 1; i <= n; i++) Read(w[i]);
	for(int i = 1, u, v; i <= m; i++)
		Read(u, v), Addedge1(u, v);
	Tarjan(1);
	dfs1(1, 0); dfs2(1, 1);
	tree1.Build(1, 1, tim2);
	tree2.Build();
	for(int i = 2; i <= n; i++) tree2.Insert(tree2.root[fa[i] - n], w[i], 0);
	for(int i = 1; i <= n; i++) tree1.Update(1, dfn2[i], w[i]);
	for(int i = n + 1; i <= cnt; i++)
		tree1.Update(1, dfn2[i], tree2.S(tree2.root[i - n]) == 2 ? INF : tree2.Query_Min(tree2.root[i - n]));
	char opt[2];
	for(int i = 1, a, b; i <= q; i++) {
		scanf("%s", opt), Read(a, b);
		if(opt[0] == 'A') printf("%d
", Min_Past(a, b));
		else {
			if(a != 1) {
				tree2.Remove(tree2.root[fa[a] - n], w[a]);
				tree2.Insert(tree2.root[fa[a] - n], b, 0);
				int tmp = tree2.Query_Min(tree2.root[fa[a] - n]);
				tree1.Update(1, dfn2[fa[a]], tmp);
			}
			tree1.Update(1, dfn2[a], b), w[a] = b;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/C202202chenkelin/p/14668908.html