Tourists

一道圆方树恶心题,*3200,不知道为什么不评黑。

这道题很容易直接想到圆方树:因为两个操作如果在树上,都需要树链剖分 + 线段树维护。而将这么一个普通图转化为一棵树,也就只有圆方树这种形式了。

于是就可以综合使用圆方树 + 线段树 + 树链剖分轻松解决了这道题,码长达到了 3.95KB,恐怖。

#include <bits/stdc++.h>
#define int long long
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define mid ((l+r)>>1)
using namespace std;
int n, m, q;
const int N = 200010, INF = 1e18;
int val[N], cnt;
multiset<int> st[N];

struct segment { //线段树
	int seg[N * 4], w[N * 4];
	void build(int now, int l, int r) {
		if (l == r) {
			seg[now] = w[l];
			return ;
		}
		build(ls(now), l, mid);
		build(rs(now), mid + 1, r);
		seg[now] = min(seg[ls(now)], seg[rs(now)]);
	}
	int query(int now, int l, int r, int ql, int qr) {
		if (l >= ql && r <= qr)
			return seg[now];
		int ans = 1e9;
		if (ql <= mid)
			ans = min(ans, query(ls(now), l, mid, ql, qr));
		if (qr > mid)
			ans = min(ans, query(rs(now), mid + 1, r, ql, qr));
		return ans;
	}
	void update(int now, int l, int r, int pos, int val) {
		if (l == r) {
			seg[now] = val;
			return ;
		}
		if (pos <= mid)
			update(ls(now), l, mid, pos, val);
		else
			update(rs(now), mid + 1, r, pos, val);
		seg[now] = min(seg[ls(now)], seg[rs(now)]);
	}
} sk;

struct tree { //树链剖分
	vector<int> v[N];
	int fa[N], dep[N], son[N], siz[N];
	int dfn[N], ind, top[N];
	void add(int x, int y) {
		v[x].push_back(y);
	}
	void dfs1(int u, int pre) {
		siz[u] = 1;
		son[u] = 0;
		int mx = 0;
		for (auto i : v[u]) {
			if (dep[i] || i == pre)
				continue;
			dep[i] = dep[u] + 1;
			fa[i] = u;
			dfs1(i, u);
			siz[u] += siz[i];
			if (son[u] == 0 || siz[i] > mx)
				mx = siz[i], son[u] = i;
		}
	}
	void dfs2(int u, int tp) {
		top[u] = tp;
		dfn[u] = ++ind;
		if (son[u] == 0)
			return ;
		dfs2(son[u], tp);
		for (auto i : v[u]) {
			if (i == son[u] || i == fa[u])
				continue;
			dfs2(i, i);
		}
	}
	void init() {
		dep[1] = top[1] = 1;
		fa[1] = ind = 0;
		dfs1(1, 0);
//		cout << "I AK IOI!";
		dfs2(1, 1);
		for (int i = 1; i <= n; i++)
			sk.w[dfn[i]] = val[i];
		for (int i = n + 1; i <= cnt; i++) {
			for (auto j : v[i])
				if (j != fa[i])
					st[i].insert(val[j]);
			if (st[i].empty())
				val[i] = INF;
			else
				val[i] = *st[i].begin();
			sk.w[dfn[i]] = val[i];
		}
		sk.build(1, 1, cnt);
	}
	void modify(int x, int y) {
		if (fa[x] > 0) {
			st[fa[x]].erase(st[fa[x]].find(val[x]));
			st[fa[x]].insert(y);
			val[fa[x]] = *st[fa[x]].begin();
			sk.update(1, 1, cnt, dfn[fa[x]], val[fa[x]]);
		}
		val[x] = y;
		sk.update(1, 1, cnt, dfn[x], y);
	}
	int query(int x, int y) {
		int ret = INF;
		while (top[x] != top[y]) {
//			cout << x << " " << y << endl;
			if (dep[top[x]] < dep[top[y]])
				swap(x, y);
			ret = min(ret, sk.query(1, 1, cnt, dfn[top[x]], dfn[x]));
			x = fa[top[x]];
		}
		ret = min(ret, sk.query(1, 1, cnt, min(dfn[x], dfn[y]), max(dfn[x], dfn[y])));
		if (x > n)
			ret = min(ret, val[fa[x]]);
		return ret;
	}
} t;

struct tarj {
	int low[N], dfn[N];
	int ind = 0;
	vector<int> v[N];
	stack<int> stk;
	void add(int x, int y) {
		v[x].push_back(y);
	}
	void tarjan(int u, int pre) {
		low[u] = dfn[u] = ++ind;
		stk.push(u);
//		cout << u << endl;
		for (auto i : v[u]) {
//			cout << i << " ";
			if (i == pre)
				continue;
			if (!dfn[i]) {
				tarjan(i, u);
				low[u] = min(low[u], low[i]);
				if (low[i] >= dfn[u]) {
					cnt++;
					val[cnt] = INF;
					t.add(u, cnt);
					t.add(cnt, u);
					val[cnt] = min(val[cnt], val[u]);
					int x;
					do {
						x = stk.top();
						if (x == u)
							break;
						stk.pop();
						t.add(cnt, x);
						t.add(x, cnt);
					} while (x != i && !stk.empty());
				}
			} else
				low[u] = min(low[u], dfn[i]);
		}
//		cout << endl;
	}
} tj;

signed main() {
	cin >> n >> m >> q;
	cnt = n;
	for (int i = 1; i <= n; i++)
		cin >> val[i];
	for (int i = 1; i <= m; i++) {
		int x, y;
		cin >> x >> y;
		tj.add(x, y);
		tj.add(y, x);
	}
	tj.tarjan(1, 0);
	t.init();
	while (q--) {
		char c;
		cin >> c;
		int x, y;
		cin >> x >> y;
		if (c == 'C')
			t.modify(x, y);
		else
			cout << t.query(x, y) << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值