[CF487E]Tourists

题意:一个无向连通图,点有点权,支持单点修改和查询,查询$(x,y)$是找出一条$x$到$y$的简单路径使得路径点权最小值最小,输出这个最小值

码农题...而且细节很多...

先找边双连通分量缩点,对于每个边双,新建一个节点和边双中的每个点连边,不属于任何边双的边就直接连,这样可以建出一棵树,然后就变成树上的问题了,因为进入一个边双就可以遍历它的所有点

对于新建的点,开一个multiset存它儿子的权值,它自己的权值设为multiset的最小值

查询就是路径$\min$,注意如果lca是新建点,那么这个lca的父亲的权值也应该被统计

修改就修改单点,如果父亲是新建点就更新它的multiset还有权值

注意在tarjan时栈中保存的应该是边而不是点

#include<stdio.h>
#include<vector>
#include<set>
using namespace std;
const int inf=2147483647;
int v[100010],n,m,q,C;
struct tree{
	int h[200010],nex[400010],to[400010],M;
	multiset<int>s[200010];
	void ins(int a,int b){
		M++;
		to[M]=b;
		nex[M]=h[a];
		h[a]=M;
	}
	void add(int a,int b){
		ins(a,b);
		ins(b,a);
	}
	int fa[200010][18],dep[200010];
	void dfs(int x){
		dep[x]=dep[fa[x][0]]+1;
		for(int i=h[x];i;i=nex[i]){
			if(to[i]!=fa[x][0]){
				if(x>n)s[x].insert(v[to[i]]);
				fa[to[i]][0]=x;
				dfs(to[i]);
			}
		}
		if(x>n)v[x]=*s[x].begin();
	}
	int lca(int x,int y){
		int i;
		if(dep[x]<dep[y])swap(x,y);
		for(i=17;i>=0;i--){
			if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
		}
		if(x==y)return x;
		for(i=17;i>=0;i--){
			if(fa[x][i]!=fa[y][i]){
				x=fa[x][i];
				y=fa[y][i];
			}
		}
		return fa[x][0];
	}
	int siz[200010],son[200010],pos[200010],bl[200010];
	void dfs1(int x){
		int i,k=0;
		siz[x]=1;
		for(i=h[x];i;i=nex[i]){
			if(to[i]!=fa[x][0]){
				dfs1(to[i]);
				siz[x]+=siz[to[i]];
				if(siz[to[i]]>siz[k])k=to[i];
			}
		}
		son[x]=k;
	}
	void dfs2(int x,int chain){
		pos[x]=++M;
		bl[x]=chain;
		if(son[x])dfs2(son[x],chain);
		for(int i=h[x];i;i=nex[i]){
			if(to[i]!=fa[x][0]&&to[i]!=son[x])dfs2(to[i],to[i]);
		}
	}
	int T[800010];
	void modify(int x,int v){
		T[x+=M]=v;
		for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]);
	}
	int query(int s,int t){
		int res=inf;
		for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
			if(~s&1)res=min(res,T[s^1]);
			if(t&1)res=min(res,T[t^1]);
		}
		return res;
	}
	void gao(){
		int i,j;
		dfs(1);
		for(j=1;j<18;j++){
			for(i=1;i<=C;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
		}
		dfs1(1);
		M=0;
		dfs2(1,1);
		for(M=1;M<=C+1;M<<=1);
		for(i=0;i<M;i++)T[i+M]=inf;
		for(i=1;i<=C;i++)T[pos[i]+M]=v[i];
		for(i=M-1;i>0;i--)T[i]=min(T[i<<1],T[i<<1|1]);
	}
	int querych(int x,int y){
		int res=inf;
		while(bl[x]!=bl[y]){
			if(dep[bl[x]]<dep[bl[y]])swap(x,y);
			res=min(res,query(pos[bl[x]],pos[x]));
			x=fa[bl[x]][0];
		}
		if(pos[x]>pos[y])swap(x,y);
		res=min(res,query(pos[x],pos[y]));
		if(x>n)res=min(res,v[fa[x][0]]);
		return res;
	}
	void modifypt(int x,int d){
		if(fa[x][0]&&fa[x][0]>n){
			s[fa[x][0]].erase(s[fa[x][0]].find(v[x]));
			s[fa[x][0]].insert(d);
			modify(pos[fa[x][0]],*s[fa[x][0]].begin());
		}
		v[x]=d;
		modify(pos[x],d);
	}
}tr;
struct graph{
	int h[100010],nex[200010],to[200010],M;
	graph(){M=1;}
	int fix(int i){return i&1?i^1:i;}
	void add(int a,int b){
		M++;
		to[M]=b;
		nex[M]=h[a];
		h[a]=M;
	}
	int dfn[100010],low[100010],stk[100010],vis[100010],tp,K;
	bool ins[200010];
	vector<int>V;
	void dfs(int x){
		int i,j,t,n,m;
		dfn[x]=low[x]=++M;
		for(i=h[x];i;i=nex[i]){
			if(!dfn[to[i]]){
				ins[stk[++tp]=fix(i)]=1;
				dfs(to[i]);
				low[x]=min(low[x],low[to[i]]);
				if(low[to[i]]>=dfn[x]){
					K++;
					V.clear();
					j=tp;
					m=0;
					do{
						t=stk[j--];
						m++;
						if(vis[to[t]]!=K){
							vis[to[t]]=K;
							V.push_back(to[t]);
						}
						if(vis[to[t^1]]!=K){
							vis[to[t^1]]=K;
							V.push_back(to[t^1]);
						}
					}while(t!=fix(i));
					n=V.size();
					if(m<n)
						tr.add(x,to[i]);
					else{
						C++;
						for(int x:V)tr.add(x,C);
					}
					do{
						ins[t=stk[tp--]]=0;
					}while(t!=fix(i));
				}
			}else{
				low[x]=min(low[x],dfn[to[i]]);
				if(!ins[fix(i)]&&dfn[to[i]]<dfn[x])ins[stk[++tp]=fix(i)]=1;
			}
		}
	}
	void gao(){
		int i,x,y;
		for(i=1;i<=n;i++)scanf("%d",v+i);
		for(i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			add(x,y);
			add(y,x);
		}
		M=0;
		C=n;
		dfs(1);
	}
}g;
int main(){
	char s[5];
	int x,y;
	scanf("%d%d%d",&n,&m,&q);
	g.gao();
	tr.gao();
	while(q--){
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C')
			tr.modifypt(x,y);
		else
			printf("%d\n",tr.querych(x,y));
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/9480473.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值