CF487E Tourists

\(\text{Problem}\)

给定一张简单无向连通图,要求支持两种操作:
\(1.\) 修改一个点的点权。
\(2.\) 询问两点之间所有简单路径上点权的最小值。

\(\text{Solution}\)

不难想到建圆方树处理
那么两点之间的答案为圆方树上圆点和方点所连着的圆点的最小权值
于是试着一个方点权值维护与其相邻圆点的最小权值
发现修改时一个圆点可影响到方点数量过多,不好维护
考虑一个方点只维护向下的圆点的最小权值
那么修改只涉及圆点及其父亲方点
具体来说就是用树链剖分和线段树维护区间最小值
因为修改不是取 \(min\),方点要存与其相连圆点的点权,要支持加入删除取最小值
因为有点权可重,使用 \(\text{multiset}\) 维护即可

\(\text{Code}\)

#include <cstdio>
#include <iostream> 
#include <set>
#define RE register
#define IN inline
using namespace std;

const int N = 2e5 + 5, INF = 2e9;
int n, m, q, cnt, dfc, tp, tot1, tot2;
int a[N], dfn[N], low[N], stk[N], h1[N], h2[N], siz[N], dep[N], top[N], fa[N], son[N];
multiset<int> S[N];
struct edge{int to, nxt;}e1[N * 5], e2[N * 5];
IN void add1(int x, int y){e1[++tot1] = edge{y, h1[x]}, h1[x] = tot1;}
IN void add2(int x, int y){e2[++tot2] = edge{y, h2[x]}, h2[x] = tot2;}

void Tarjan(int x)
{
	dfn[x] = low[x] = ++dfc, stk[++tp] = x;
	for(RE int i = h1[x]; i; i = e1[i].nxt)
	{
		int v = e1[i].to;
		if (!dfn[v])
		{
			Tarjan(v), low[x] = min(low[x], low[v]);
			if (dfn[x] == low[v])
			{
				++cnt, add2(cnt, x), add2(x, cnt);
				for(RE int u = 0; u ^ v; --tp)
					u = stk[tp], add2(cnt, u), add2(u, cnt);
			}
		}
		else low[x] = min(low[x], dfn[v]);
	}
}

struct SegmentTree{
	#define ls (p << 1)
	#define rs (ls | 1)
	int seg[N * 4];
	void build(int p, int l, int r)
	{
		seg[p] = INF;
		if (l == r) return;
		int mid = l + r >> 1;
		build(ls, l, mid), build(rs, mid + 1, r);
	}
	void Modify(int p, int l, int r, int x, int v)
	{
		if (l == r) return seg[p] = v, void();
		int mid = l + r >> 1;
		if (x <= mid) Modify(ls, l, mid, x, v);
		else Modify(rs, mid + 1, r, x, v);
		seg[p] = min(seg[ls], seg[rs]);
	}
	int Query(int p, int l, int r, int x, int y)
	{
		if (x > r || y < l) return INF;
		if (x <= l && r <= y) return seg[p];
		int mid = l + r >> 1, res = INF;
		if (x <= mid) res = Query(ls, l, mid, x, y);
		if (y > mid) res = min(res, Query(rs, mid + 1, r, x, y));
		return res;
	}
}T;

void Dfs1(int x)
{
	siz[x] = 1;
	for(RE int i = h2[x]; i; i = e2[i].nxt)
	{
		int v = e2[i].to;
		if (v == fa[x]) continue;
		fa[v] = x, dep[v] = dep[x] + 1, Dfs1(v), siz[x] += siz[v];
		if (siz[son[x]] < siz[v]) son[x] = v;
	}
}
void Dfs2(int x, int t)
{
	top[x] = t, dfn[x] = ++dfc;
	if (son[x]) Dfs2(son[x], t);
	for(RE int i = h2[x]; i; i = e2[i].nxt)
	{
		int v = e2[i].to;
		if (v == fa[x] || v == son[x]) continue;
		Dfs2(v, v);
	}
}
IN int Query(int x, int y)
{
	int fx = top[x], fy = top[y], res = INF;
	while (fx ^ fy)
		if (dep[fx] > dep[fy]) res = min(res, T.Query(1, 1, cnt, dfn[fx], dfn[x])), x = fa[fx], fx = top[x];
		else res = min(res, T.Query(1, 1, cnt, dfn[fy], dfn[y])), y = fa[fy], fy = top[y];
	if (dep[x] > dep[y]) swap(x, y);
	if (x > n && fa[x]) res = min(res, a[fa[x]]);
	return min(res, T.Query(1, 1, cnt, dfn[x], dfn[y]));
}

int main()
{
	scanf("%d%d%d", &n, &m, &q);
	for(RE int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(RE int i = 1, x, y; i <= m; i++) scanf("%d%d", &x, &y), add1(x, y), add1(y, x);
	cnt = n, Tarjan(1), Dfs1(1), dfc = 0, Dfs2(1, 1), T.build(1, 1, cnt);
	for(RE int i = 1; i <= n; i++){T.Modify(1, 1, cnt, dfn[i], a[i]); if (fa[i]) S[fa[i]].insert(a[i]);}
	for(RE int i = n + 1; i <= cnt; i++) a[i] = *S[i].begin(), T.Modify(1, 1, cnt, dfn[i], a[i]);
	char op[3];
	for(RE int x, y; q; --q)
	{
		scanf("%s%d%d", op, &x, &y);
		if (op[0] == 'A') printf("%d\n", Query(x, y));
		else{
			T.Modify(1, 1, cnt, dfn[x], y);
			if (fa[x])
			{
				S[fa[x]].erase(S[fa[x]].lower_bound(a[x])), S[fa[x]].insert(y);
				if (a[fa[x]] != *S[fa[x]].begin())
					a[fa[x]] = *S[fa[x]].begin(), T.Modify(1, 1, cnt, dfn[fa[x]], a[fa[x]]);
			}
			a[x] = y;
		}
	}
}
posted @ 2022-01-24 22:31  leiyuanze  阅读(27)  评论(0编辑  收藏  举报