CF487E Tourists

板子大集合?那我不看题解也没做出来/kk
思路不算难,但是实现起来比较码农

tarjan 建广义圆方树 + 对圆方树树剖 + 用 multiset 维护每个双联通分量最小值

传送门
给出一个图,每个点有一个权值
现在给 (q) 个操作

  • 更改一个点的权值
  • 查询 (x)(y) 的所有简单路径(不能有重复的点)中,能走到的最小权值是多少

首先,要会用 multiset,还要会tarjan,并且会用他构建广义圆方树,这里

再来看这个题,可以猜出 一个结论:一个点双连通图(分量),对于其中任意三个点 (a,b,c),一定能找出一条 (a,b) 间的简单路径,使得其穿过 (c)
因为一个点双应该是一个环,然后再加上一堆边组成的,所以找到上面要求的这样一个路径其实很容易,所以就 感性理解 一下就好
比较详细的证明CF官方题解上有

那么,我们只要走到一个点双中,就直接取这个点双的最小值就行了,然后可以继续往前走出去
所以就建一棵圆方树就好了,每个方点维护这个点双中的最小值
对每个方点,还要用一个 multiset(自带排序)来存这个点双的所有点的权值,方便删除和添加新权值来实现更改

对于查询,就直接查这两个点在树上的路径中权值最小值就行了,用树剖维护
考虑怎么修改
发现如果像上面那样维护,每次修改一个圆点权值,都会波及到他周围所有的方点,容易被菊花图卡飞
所以,每个方点的权值应该是:他所有相邻的圆点(就是他所对应的那个点双中的所有点),除了它在圆方树上的父亲,中的点权最小值
这样,修改操作时,每个圆点被修改,都只会波及到它的父亲,就只去更改一下父亲的信息就行了
查询时也要加一个,就是如果 (x,y) 的 LCA 是个方点,那么还要和它的父亲(是个圆点)的权值取 (min)

( exttt{code.}) 写了我一上午/kk

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<set>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 200006
#define M 400006
int n,m;
int w[N];
std::multiset<int>set[N];
struct graph{
	int fir[N],nex[M],to[M],tot;
	inline void add(int u,int v){
		to[++tot]=v;
		nex[tot]=fir[u];fir[u]=tot;
	}
}G,T;
struct get_bcc{
	int top,stack[N];
	int dfn[N],low[N],dfscnt;
	int bcccnt;
	void tarjan(int u){
		dfn[u]=low[u]=++dfscnt;stack[top++]=u;
		for(reg int v,i=G.fir[u];i;i=G.nex[i]){
			v=G.to[i];
			if(!dfn[v]){
				tarjan(v);low[u]=std::min(low[u],low[v]);
				if(low[v]>=dfn[u]){
					bcccnt++;
					do{
						T.add(bcccnt,stack[--top]);T.add(stack[top],bcccnt);
					}while(stack[top]^v);
					T.add(bcccnt,u);T.add(u,bcccnt);
				}
			}
			else low[u]=std::min(low[u],dfn[v]);
		}
	}
}BCC;
struct TREE{
	int fa[N],deep[N],size[N],son[N],index[N];
	int top[N],dfn[N],dfscnt;
	void dfs1(int u,int fat){
		fa[u]=fat;deep[u]=deep[fat]+1;size[u]=1;
		for(reg int v,i=T.fir[u];i;i=T.nex[i]){
			v=T.to[i];
			if(v!=fat){
				dfs1(v,u);size[u]+=size[v];
				if(size[v]>size[son[u]]) son[u]=v;
			}
		}
	}
	void dfs2(int u,int topnow){
		top[u]=topnow;dfn[u]=++dfscnt;index[dfscnt]=u;
		if(!son[u]) return;
		dfs2(son[u],topnow);
		for(reg int v,i=T.fir[u];i;i=T.nex[i]){
			v=T.to[i];
			if(!dfn[v]) dfs2(v,v);
		}
	}
	struct tr {
		tr *ls,*rs;
		int min;
	}dizhi[N<<1],*root=&dizhi[0];
	int tot;
	inline void pushup(tr *tree){tree->min=std::min(tree->ls->min,tree->rs->min);}
	void build(tr *tree,int l,int r){
		if(l==r) return tree->min=w[index[l]],void();
		int mid=(l+r)>>1;
		tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
		build(tree->ls,l,mid);build(tree->rs,mid+1,r);
		pushup(tree);
	}
	void change(tr *tree,int l,int r,int pos,int k){
		if(l==r) return tree->min=k,void();
		int mid=(l+r)>>1;
		if(pos<=mid) change(tree->ls,l,mid,pos,k);
		else change(tree->rs,mid+1,r,pos,k);
		pushup(tree);
	}
	int get_min(tr *tree,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return tree->min;
		int mid=(l+r)>>1;
		if(qr<=mid) return get_min(tree->ls,l,mid,ql,qr);
		if(ql>mid) return get_min(tree->rs,mid+1,r,ql,qr);
		return std::min(get_min(tree->ls,l,mid,ql,qr),get_min(tree->rs,mid+1,r,ql,qr));
	}
	inline int find(int x,int y){
		int ret=0x7f7f7f7f;
		while(top[x]!=top[y]){
			if(deep[top[x]]<deep[top[y]]) x^=y,y^=x,x^=y;
			ret=std::min(ret,get_min(root,1,BCC.bcccnt,dfn[top[x]],dfn[x]));
			x=fa[top[x]];
		}
		if(deep[x]>deep[y]) x^=y,y^=x,x^=y;
		ret=std::min(ret,get_min(root,1,BCC.bcccnt,dfn[x],dfn[y]));
		if(x>n) ret=std::min(ret,w[fa[x]]);
		return ret;
	}
}Tree;
int main(){
//		std::freopen("tmp.txt","r",stdin);
	BCC.bcccnt=n=read();m=read();int q=read();
	for(reg int i=1;i<=n;i++) w[i]=read();
	for(reg int u,v,i=1;i<=m;i++){
		u=read();v=read();
		G.add(u,v);G.add(v,u);
	}
	BCC.tarjan(1);
//		std::printf("bcccnt : %d
tot of edges : %d

",BCC.bcccnt,T.tot);
	Tree.dfs1(1,0);
	Tree.dfs2(1,1);
	for(reg int i=2;i<=n;i++) set[Tree.fa[i]-n].insert(w[i]);
	//一定从 2 开始循环,因为 1 是跟,用它的父亲(其实他也没有父亲,这里说的是它的 fa 数组),减 n 减成负数,RE 
	for(reg int i=n+1;i<=BCC.bcccnt;i++)
		w[i]=set[i-n].empty()?0x7f7f7f7f:*set[i-n].begin();
	Tree.build(Tree.root,1,BCC.bcccnt);
	reg int x,y;char op;
	while(q--){
		std::scanf("%c",&op);
		x=read();y=read();
		if(op=='C'){
			Tree.change(Tree.root,1,BCC.bcccnt,Tree.dfn[x],y);
			if(x==1){w[1]=y;continue;}
			int ind=Tree.fa[x]-n;
			set[ind].erase(set[ind].find(w[x]));
			set[ind].insert(y);
			int min=*set[ind].begin();
			w[x]=y;
			if(min!=w[ind+n]) Tree.change(Tree.root,1,BCC.bcccnt,Tree.dfn[ind+n],min);
			w[ind+n]=min;
		}
		else std::printf("%d
",Tree.find(x,y));
	}
	return 0;
}
```CF487E Tourists
原文地址:https://www.cnblogs.com/suxxsfe/p/12766910.html