• [题解] 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;
    }
    
  • 相关阅读:
    Python爬虫入门教程 53-100 Python3爬虫获取三亚天气做旅游参照
    计算机大专学历,2019如何应届毕业薪水过万!
    Python爬虫入门教程 52-100 Python3爬虫获取博客园文章定时发送到邮箱
    Python爬虫入门教程 51-100 Python3爬虫通过m3u8文件下载ts视频-Python爬虫6操作
    Python爬虫入门教程 50-100 Python3爬虫爬取VIP视频-Python爬虫6操作
    nvm-windows
    如何进行 IIS Log 分析
    常用 Jenkins 配置
    Jenkins 插件安装问题
    常用 SQL Server 脚本
  • 原文地址:https://www.cnblogs.com/C202202chenkelin/p/14668908.html
Copyright © 2020-2023  润新知