CF487E Tourists

I.CF487E Tourists

用这题作圆方树的入门题还是很合适的。

首先,先建出圆方树出来。我们可以给方点赋一个权值,即为它连着的所有圆点的权值的\(\min\)。然后只需要在圆方树上查询路径\(\min\)即可。使用树剖即可。

但这个做法会被叉掉:当原图是一张菊花图时,花心的圆点将会连向\(n-1\)个方点,也就意味着修改该点权值时会影响\(n-1\)个方点,就可以把复杂度叉到\(O(n^2)\)

有什么办法呢?

考虑到树的性质。我们可以在每个方节点开一个std::multiset<int>,储存它所有儿子的值。则方点的权值即为multiset中的最小值。这样,修改一个圆点时,就只需要改动它父亲的multiset即可。

然后,在询问时,如果路径的LCA是一个方点,将答案与该方点的父亲的权值取\(\min\)即可。

这个算法利用了圆方树中的圆点会被很多方点统计的性质,最终让每个圆点仅被一个方点计入,不重不漏,非常神仙。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,q,c,val[200100];
namespace SCT{//square circle tree?
	int dfn[200100],rev[200100],son[200100],sz[200100],fa[200100],top[200100],dep[200100],tot,mn[800100];
	vector<int>v[200100];
	void ae(int x,int y){
		v[x].push_back(y),v[y].push_back(x);
	}
	multiset<int>s[100100];
	void dfs1(int x,int Fa){
		fa[x]=Fa,dep[x]=dep[Fa]+1,sz[x]=1;
		for(auto y:v[x]){
			if(y==Fa)continue;
			dfs1(y,x);
			if(x>n)s[x-n].insert(val[y]);
			sz[x]+=sz[y];
			if(sz[y]>sz[son[x]])son[x]=y;
		}
		if(x>n)val[x]=*s[x-n].begin();
	}
	void dfs2(int x){
		dfn[x]=++tot,rev[tot]=x;
		if(son[x])top[son[x]]=top[x],dfs2(son[x]);
		for(auto y:v[x])if(y!=fa[x]&&y!=son[x])top[y]=y,dfs2(y);
	}
	#define lson x<<1
	#define rson x<<1|1
	#define mid ((l+r)>>1)
	void pushup(int x){mn[x]=min(mn[lson],mn[rson]);}
	void build(int x,int l,int r){
		if(l==r){mn[x]=val[rev[l]];return;}
		build(lson,l,mid),build(rson,mid+1,r),pushup(x);
	}
	int query(int x,int l,int r,int L,int R){
		if(l>R||r<L)return 0x3f3f3f3f;
		if(L<=l&&r<=R)return mn[x];
		return min(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
	}
	void update(int x,int l,int r,int P){
		if(l>P||r<P)return;
		if(l==r){mn[x]=val[rev[P]];return;}
		update(lson,l,mid,P),update(rson,mid+1,r,P),pushup(x);
	}
	#undef lson
	#undef rson
	#undef mid
	void prepare(){
		dfs1(1,0),top[1]=1,dfs2(1);
//		for(int x=1;x<=c;x++)printf("%d::FA:%d SN:%d SZ:%d DP:%d TP:%d DN:%d RV:%d\n",x,fa[x],son[x],sz[x],dep[x],top[x],dfn[x],rev[x]);
		build(1,1,c);
	}
	int ask(int x,int y){
		int res=0x3f3f3f3f;
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			res=min(res,query(1,1,c,dfn[top[x]],dfn[x])),x=fa[top[x]];
		}
		if(dep[x]>dep[y])swap(x,y);
		res=min(res,query(1,1,c,dfn[x],dfn[y]));
		if(x>n)res=min(res,val[fa[x]]);
//		printf("LCA:%d\n",x);
		return res;
	}
	void modify(int x,int y){
		if(fa[x])s[fa[x]-n].erase(s[fa[x]-n].find(val[x]));
		val[x]=y;
		if(fa[x])s[fa[x]-n].insert(val[x]),val[fa[x]]=*s[fa[x]-n].begin(),update(1,1,c,dfn[fa[x]]);
		update(1,1,c,dfn[x]);
//		for(int i=1;i<=c;i++)printf("%d:%d ",i,val[i]);puts("");
	}
}
namespace Graph{
	vector<int>v[100100];
	int dfn[100100],low[100100],tot;
	stack<int>s;
	void Tarjan(int x){
		dfn[x]=low[x]=++tot,s.push(x);
		for(auto y:v[x]){
			if(!dfn[y]){
				Tarjan(y);
				low[x]=min(low[x],low[y]);
				if(low[y]>=dfn[x]){
					c++;
					while(s.top()!=y)SCT::ae(c,s.top()),s.pop();
					SCT::ae(c,s.top()),s.pop();
					SCT::ae(c,x);
				}
			}else low[x]=min(low[x],dfn[y]);
		}
	}
}
char s[10];
int main(){
	scanf("%d%d%d",&n,&m,&q),c=n;
	for(int i=1;i<=n;i++)scanf("%d",&val[i]);
	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),Graph::v[x].push_back(y),Graph::v[y].push_back(x);
	Graph::Tarjan(1);
	SCT::prepare();
	for(int i=1,x,y;i<=q;i++){
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C')SCT::modify(x,y);
		else printf("%d\n",SCT::ask(x,y));
	}
	return 0;
}

posted @ 2021-04-06 13:39  Troverld  阅读(43)  评论(0编辑  收藏  举报