• [CF487E]Tourists


    题目大意:给定一张图(保证连通),每个点有点权。现在有两种操作:

    1. $C;a;w:$把$a$的点权改为$w$;
    2. $A;a;b:$询问从$a$到$b$的所有简单路径(不经过重复点)中,点权最小的点的点权。

    题解:可以发现如果是一棵树,直接用树链剖分维护最值即可。

    但是它是一个图,所以可以想到缩点。

    题目要求不经过重复点,发现对于一个点双连通分量,如果到了其中的任意一个点,一定可以走到其中的点权最小的点。

    于是想到把图中的点双缩点,维护圆方树,把方点的值设为它周围的圆点中点权最小的点的点权,树连剖分。

    但是这样做有一个问题,每次修改一个圆点,都要修改周围的所有方点,如果一个点在多个点双中,每次修改的时间复杂度可以达到$nlog_2 n$级别,就会$TLE$

    怎么办呢?

    这时我们改变一下方点的值,可以把方点中的值改为它的儿子中最小值(而不是周围,也就是说,它的父亲的值不会影响这个方点)

    看起来没什么是吧?

    但是这样我们可以用$multiset$(**不是set**)来存它的每个儿子的值,修改一个圆点时,我们只修改这个圆点本身和这个点的父亲(也就是一个方点)的信息(注意如果修改的点是根就不修改父亲)。

    修改复杂度$O(log_2 n)$(有可能是假的,我并不怎么会推)

    询问时,像原来一样树链剖分,注意如果最后的节点是一个方点(也就是说查询的两个点的$LCA$是方点),那么这个方点的父亲就没有统计到答案中,但是它是有贡献的,于是要计算

    时间复杂度$(O(nlog_2^2 n)$。

    卡点:1.不会$tarjan$求点双

      2.暴力修改$TLE$

    C++ Code:

    #include <cstdio>
    #include <set>
    
    #define maxn 100010
    #define maxm 100010
    
    #define N 200010 
    #define M 200010
    
    using namespace std;
    const int inf = 0x7fffffff;
    inline int min(int a, int b) {return a < b ? a : b;}
    inline void swap(int &a, int &b) {a ^= b ^= a ^= b;}
    int n, m, q, cnt;
    int w[N];
    struct Edge {int to, nxt;};
    multiset <int> SM[N];
    struct ST {
    	int V[N << 1], W[N];
    	void build(int rt, int l, int r) {
    		if (l == r) {
    			V[rt] = W[l];
    			return ;
    		}
    		int mid = l + r >> 1;
    		build(rt << 1, l, mid);
    		build(rt << 1 | 1, mid + 1, r);
    		V[rt] = min(V[rt << 1], V[rt << 1 | 1]);
    	}
    	void add(int rt, int l, int r, int x, int num) {
    		if (l == r) {
    			V[rt] = num;
    			return ;
    		}
    		int mid = l + r >> 1;
    		if (x <= mid) add(rt << 1, l, mid, x, num);
    		else add(rt << 1 | 1, mid + 1, r, x, num);
    		V[rt] = min(V[rt << 1], V[rt << 1 | 1]);
    	}
    	int ask(int rt, int l, int r, int L, int R) {
    		if (L <= l && R >= r) {
    			return V[rt];
    		}
    		int mid = l + r >> 1, ans = inf;
    		if (L <= mid) ans = ask(rt << 1, l, mid, L, R);
    		if (R > mid) ans = min(ans, ask(rt << 1 | 1, mid + 1, r, L, R));
    		return ans;
    	}
    } S;
    struct Tree {
    	int head[N], cntE;
    	Edge e[M << 1];
    	void addE(int a, int b) {
    		e[++cntE] = (Edge) {b, head[a]}; head[a] = cntE;
    	}
    	int idx;
    	int fa[N], dep[N], dfn[N], son[N], sz[N], top[N];
    	void dfs1(int rt) {
    		sz[rt] = 1;
    		son[rt] = 0;
    		for (int i = head[rt]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if (!dep[v]) {
    				dep[v] = dep[rt] + 1;
    				fa[v] = rt;
    				dfs1(v);
    				sz[rt] += sz[v];
    				if (!son[rt] || sz[v] > sz[son[rt]]) son[rt] = v;
    			}
    		}
    	}
    	void dfs2(int rt) {
    		dfn[rt] = ++idx;
    		int v = son[rt];
    		if (v) top[v] = top[rt], dfs2(v);
    		for (int i = head[rt]; i; i = e[i].nxt) {
    			v = e[i].to;
    			if (v != fa[rt] && v != son[rt]) {
    				top[v] = v;
    				dfs2(v);
    			}
    		}
    	}
    	void init(int rt) {
    		int v;
    		fa[rt] = idx = 0;
    		dep[top[rt] = rt] = 1;
    		dfs1(rt);
    		dfs2(rt);
    		for (int i = 1; i <= n; i++) S.W[dfn[i]] = w[i];
    		for (int i = n + 1; i <= cnt; i++) {
    			for (int j = head[i]; j; j = e[j].nxt) {
    				v = e[j].to;
    				if (v != fa[i]) SM[i].insert(w[v]);
    			}
    			if (SM[i].empty()) w[i] = inf;
    			else w[i] = *SM[i].begin();
    			S.W[dfn[i]] = w[i];
    		}
    		S.build(1, 1, cnt);
    	}
    	void add(int x, int num) {
    		int f = fa[x];
    		if (f) {
    			SM[f].erase(SM[f].find(w[x]));
    			SM[f].insert(num);
    			w[f] = *SM[f].begin();
    			S.add(1, 1, cnt, dfn[f], w[f]);
    		}
    		w[x] = num;
    		S.add(1, 1, cnt, dfn[x], num);
    	}
    	int ask(int x, int y) {
    		int res = inf;
    		while (top[x] != top[y]) {
    			if (dep[top[x]] < dep[top[y]]) swap(x, y);
    			res = min(res, S.ask(1, 1, cnt, dfn[top[x]], dfn[x]));
    			x = fa[top[x]];
    		}
    		if (dep[x] > dep[y]) swap(x, y);
    		res = min(res, S.ask(1, 1, cnt, dfn[x], dfn[y]));
    		if (x > n) res = min(res, w[fa[x]]);
    		return res;
    	}
    } T;
    struct Graph {
    	int head[maxn], cntE;
    	Edge e[maxm << 1];
    	int stack[maxn], DFN[maxn], low[maxn];
    	int idx, tot;
    	bool vis[maxn];
    	void addE(int a, int b) {
    		e[++cntE] = (Edge) {b, head[a]}; head[a] = cntE;
    	}
    	void tarjan(int rt) {
    		vis[stack[++tot] = rt] = true;
    		low[rt] = DFN[rt] = ++idx;
    		int v, tmp;
    		for (int i = head[rt]; i; i = e[i].nxt) {
    			v = e[i].to;
    			if (!DFN[v]) {
    				tarjan(v);
    				low[rt] = min(low[rt], low[v]);
    				if (low[v] >= DFN[rt]) {
    					cnt++;
    					w[cnt] = inf;
    					T.addE(cnt, rt);
    					T.addE(rt, cnt);
    					w[cnt] = min(w[cnt], w[rt]);
    					do {
    						vis[tmp = stack[tot--]] = false;
    						T.addE(cnt, tmp);
    						T.addE(tmp, cnt);
    					} while(tmp != v);
    				}
    			} else {
    				low[rt] = min(low[rt], DFN[v]);
    			}
    		}
    	}
    } G;
    
    int main() {
    	scanf("%d%d%d", &n, &m, &q);
    	for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    	for (int i = 1; i <= m; i++) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		G.addE(a, b);
    		G.addE(b, a);
    	}
    	cnt = n;
    	G.tarjan(1);
    	T.init(1);
    	while (q--) {
    		char op; int x, y;
    		scanf("%s%d%d", op, &x, &y);
    		if (op[0] == 'C') {
    			T.add(x, y);
    		} else {
    			printf("%d
    ", T.ask(x, y));
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    SourceInsight工具增强——AStyle(代码格式化)、PC-Lint(静态检查)
    读《Top benefits of continuous integration》有感
    授人以鱼不如授人以渔——和女儿学一起学成语
    Linux内存使用情况以及内存泄露分析之工具与方法
    常用gcc选项
    KVM内核文档阅读笔记
    关于Linux虚拟化技术KVM的科普 科普五(From 世民谈云计算)
    关于Linux虚拟化技术KVM的科普 科普四(From humjb_1983)
    关于Linux虚拟化技术KVM的科普 科普三(From OenHan)
    关于Linux虚拟化技术KVM的科普 科普二(KVM虚拟机代码揭秘)
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9451222.html
Copyright © 2020-2023  润新知