「Codeforces487E」Tourists-圆方树

Description

给定一张简单无向连通图,要求支持两种操作:

  1. 修改一个点的点权。

  2. 询问两点之间所有简单路径上点权的最小值。

n , m , q ≤ 1 0 5 n,m,q \leq 10^5 n,m,q105

Solution

先不考虑修改。

圆方树有一个性质,那就是 ( u , v ) (u,v) (u,v)路径上的方点所代表的点双并为 ( u , v ) (u,v) (u,v)间的简单路径并。

所以只需要把方点权值设为其代表的点双中的最小值即可。

考虑修改,可以发现若直接套用上述做法,由于每个圆点连接的方点数为 O ( n ) O(n) O(n)的,所以复杂度会退化为 O ( n 2 ) O(n^2) O(n2)

考虑一个常用的套路,那就是把每个方点的权值设为它子节点的权值最小值,每次修改时只用修改父节点即可,用堆维护。

注意当 l c a lca lca为方点时,要用其父节点权值更新答案。

#include <bits/stdc++.h>
using namespace std;

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

inline char gc()
{
	char c = getchar();
	while (c < 'A' || c > 'Z') c = getchar();
	return c;
}

const int maxn = 100005;

int n, m, q, w[maxn << 1];

struct edge
{
	int to, next;
} e[maxn << 1];
int h[maxn], tot;

inline void add(int u, int v)
{
	e[++tot] = (edge) {v, h[u]}; h[u] = tot;
	e[++tot] = (edge) {u, h[v]}; h[v] = tot;
}

vector<int> to[maxn << 1];
int cnt, dep[maxn << 1], siz[maxn << 1], son[maxn << 1], ord[maxn << 1], dfn[maxn << 1], low[maxn << 1], Time, fa[maxn << 1], top[maxn << 1];

struct
{
	priority_queue<int, vector<int>, greater<int> > q1, q2;
	int Top()
	{
		while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
		return q1.top();
	}
	void insert(int x) {q1.push(x);}
	void erase(int x) {q2.push(x);}
} val[maxn];

void dfs1(int u)
{
	dep[u] = dep[fa[u]] + 1; siz[u] = 1;
	for (int v : to[u])
		if (v != fa[u]) {
			fa[v] = u; dfs1(v);
			siz[u] += siz[v];
			if (siz[v] > siz[son[u]]) son[u] = v;
			if (u > n) val[u - n].insert(w[v]);
		}
	if (u > n) w[u] = val[u - n].Top();
}

void dfs2(int u)
{
	ord[dfn[u] = ++Time] = u;
	if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
	else low[top[u]] = Time;
	
	for (int v : to[u])
		if (v != fa[u] && v != son[u]) top[v] = v, dfs2(v);
}

int rt[maxn << 1], Min[maxn << 3], lch[maxn << 3], rch[maxn << 3];

#define mid ((l + r) >> 1)

void build(int &s, int l, int r)
{
	s = ++tot;
	if (s > 2e5 * 4) puts("fuck you"), exit(0);
	if (l == r) return Min[s] = w[ord[l]], void();
	build(lch[s], l, mid);
	build(rch[s], mid + 1, r);
	Min[s] = min(Min[lch[s]], Min[rch[s]]);
}

void modify(int &s, int l, int r, int p)
{
	if (l == r) return Min[s] = w[ord[l]], void();
	if (p <= mid) modify(lch[s], l, mid, p);
	else modify(rch[s], mid + 1, r, p);
	Min[s] = min(Min[lch[s]], Min[rch[s]]);
}

int query(int s, int l, int r, int ql, int qr)
{
	if (ql <= l && r <= qr) return Min[s];
	if (qr <= mid) return query(lch[s], l, mid, ql, qr);
	else if (ql > mid) return query(rch[s], mid + 1, r, ql, qr);
	else return min(query(lch[s], l, mid, ql, qr), query(rch[s], mid + 1, r, ql, qr));
}

void modify(int v, int u)
{
	if (fa[u]) val[fa[u] - n].erase(w[u]), val[fa[u] - n].insert(v), w[fa[u]] = val[fa[u] - n].Top();
	w[u] = v; 
	modify(rt[top[u]], dfn[top[u]], low[top[u]], dfn[u]);
	if (fa[u]) u = fa[u], modify(rt[top[u]], dfn[top[u]], low[top[u]], dfn[u]);	
}

int query(int u, int v)
{
	int ans = 1e9;
	while (top[u] != top[v]) {
		if (dep[top[u]] > dep[top[v]]) ans = min(ans, query(rt[top[u]], dfn[top[u]], low[top[u]], dfn[top[u]], dfn[u])), u = fa[top[u]];
		else ans = min(ans, query(rt[top[v]], dfn[top[v]], low[top[v]], dfn[top[v]], dfn[v])), v = fa[top[v]];
	}
	if (dfn[u] > dfn[v]) swap(u, v);
	ans = min(ans, query(rt[top[v]], dfn[top[v]], low[top[v]], dfn[u], dfn[v]));
	if (u > n) ans = min(ans, w[fa[u]]);
	return ans;
}

namespace Tarjan
{

	int dfn[maxn], low[maxn], Time, stk[maxn], top;

	void dfs(int u)
	{
		dfn[u] = low[u] = ++Time;
		stk[++top] = u;
		for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
			if (!dfn[v]) {
				dfs(v);
				low[u] = min(low[u], low[v]);
				if (dfn[u] == low[v]) {
					++cnt;
					while (stk[top + 1] != v)
						to[stk[top]].push_back(cnt), to[cnt].push_back(stk[top]), --top;
					to[u].push_back(cnt), to[cnt].push_back(u);
				}
			} else low[u] = min(low[u], dfn[v]);
	}
	
}

int main()
{
	n = gi(); m = gi(); q = gi();
	for (int i = 1; i <= n; ++i) w[i] = gi();
	for (int i = 1; i <= m; ++i) add(gi(), gi());

	cnt = n;
	for (int i = 1; i <= n; ++i)
		if (!Tarjan::dfn[i]) Tarjan::dfs(i);

	for (int i = 1; i <= cnt; ++i)
		if (!fa[i]) dfs1(i);
	Time = 0;
	for (int i = 1; i <= cnt; ++i)
		if (!top[i]) top[i] = i, dfs2(i);

	tot = 0;
	for (int i = 1; i <= cnt; ++i)
		if (top[i] == i) build(rt[i], dfn[i], low[i]);

	for (int i = 1; i <= q; ++i) {
		char c = gc();
		if (c == 'C') modify(gi(), gi());
		else printf("%d\n", query(gi(), gi()));
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值