其他分享
首页 > 其他分享> > 圆方树

圆方树

作者:互联网

目录

圆方树

前言

众所周知,树往往有着许多非常好的性质

圆方树就是一种把一个图变成一棵树的方式

定义

我们一般在无向图上使用圆方树

先介绍点双连通分量

一个点双连通图的定义:图中任意两点之间都至少有两条不同的路径可互相抵达

一个点双连通分量是一个极大点双连通图

在圆方树中,每个原图中的点对应一个圆点,每个点双对应方点,每个点(圆点)和其对应的点双(方点)连边

上图就分别展示了原图,点双以及对应的圆方树

显然,圆点不和圆点连,方点不和方点连

我们可以在方点处维护有关点双内多条路径的信息

如何建树

首先我们需要跑一遍 \(tarjan\) 找出每个点双,然后将每个点双代表的方点和点双内的点代表的圆点连边,就建出了一棵圆方树

如下:

inline void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	S.push(x);
	for(auto y:G[x]){
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]==dfn[x]){
				++col_cnt;
				for(int j=0;j!=y;S.pop()){
					j=S.top();
					H[col_cnt].pb(j);
					H[j].pb(col_cnt);
				}
				H[col_cnt].pb(x);
				H[x].pb(col_cnt);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}

习题

CF487E tourists

题意

给定一张图(保证连通),每个点有点权。

现在有两种操作:

思路

在图上搞树剖的操作?

考虑先把图变成一棵树

采用圆方树,然后搞树剖

容易想到把每个方点的权值设为周围所有圆点的最小的权值,搞个 \(multiset\) 维护一下

然而暴力修改会被卡

利用圆方树是一棵树的性质

我们把每个方点的权值设为其所有儿子节点的最小值,对于每一个方点开一个 \(multiset\) 维护最值

这样就可以保证复杂度

#include<bits/stdc++.h>
using namespace std;

#define pb push_back
#define lsp p<<1
#define rsp p<<1|1
#define size pajo
#define int long long

const int N=3e5+5;
const int inf=1e9+5;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

int n,m,q,cnt,col_cnt;
int w[N];
int low[N],dfn[N];
int dep[N],f[N],size[N],son[N];
int top[N],val[N];
stack <int> S;
multiset <int> s[N];
vector <int> G[N],H[N];

struct node{
	int l,r;
	int mn;
}t[N<<2];

node operator + (node l,node r){
	return (node){l.l,r.r,min(l.mn,r.mn)};
}

inline void build(int p,int l,int r){
	if(l==r){
		t[p]={l,r,val[l]};
		return;
	}
	int mid=l+r>>1;
	build(lsp,l,mid);
	build(rsp,mid+1,r);
	t[p]=t[lsp]+t[rsp];
}

inline void update(int p,int x,int k){
	if(t[p].l==t[p].r&&t[p].l==x){
		t[p].mn=k;
		return;
	}
	int mid=t[p].l+t[p].r>>1;
	if(x<=mid) update(lsp,x,k);
	else update(rsp,x,k);
	t[p]=t[lsp]+t[rsp];
}

inline node query(int p,int l,int r){
	if(l<=t[p].l&&t[p].r<=r) return t[p];
	node res={0,0,inf};
	int mid=t[p].l+t[p].r>>1;
	if(l<=mid) res=res+query(lsp,l,r);
	if(mid<r) res=res+query(rsp,l,r);
	return res;
}

inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,size[x]=1,f[x]=fa;
	int maxson=-1;
	for(auto y:H[x]){
		if(y==fa) continue;
		dfs1(y,x);
		size[x]+=size[y];
		if(size[y]>maxson) maxson=size[y],son[x]=y;
	}
}

inline void dfs2(int x,int tp){
	dfn[x]=++cnt,top[x]=tp,val[cnt]=w[x];
	if(!son[x]) return;
	dfs2(son[x],tp);
	for(auto y:H[x]){
		if(y==f[x]||y==son[x]) continue;
		dfs2(y,y);
	}
}

inline int Q(int x,int y){
	node res=(node){0,0,inf};
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res=res+query(1,dfn[top[x]],dfn[x]);
		x=f[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	res=res+query(1,dfn[x],dfn[y]);
	if(x>n)
		res.mn=min(res.mn,w[f[x]]);
	return res.mn;
}

inline void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	S.push(x);
	for(auto y:G[x]){
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]==dfn[x]){
				++col_cnt;
				for(int j=0;j!=y;S.pop()){
					j=S.top();
					H[col_cnt].pb(j);
					H[j].pb(col_cnt);
				}
				H[col_cnt].pb(x);
				H[x].pb(col_cnt);
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}

signed main(){
	n=read(),m=read(),q=read();
	col_cnt=n;
	for(int i=1;i<=n;++i) w[i]=read();
	for(int i=1;i<=m;++i){
		int x=read(),y=read();
		G[x].pb(y);
		G[y].pb(x);
	}
	tarjan(1);
	memset(dfn,0,sizeof(dfn));
	cnt=0;
	dfs1(1,0);
	for(int i=1;i<=n;++i)
		if(f[i])
			s[f[i]].insert(w[i]);
	for(int i=n+1;i<=col_cnt;++i)
		w[i]=*s[i].begin();
	dfs2(1,1);
	build(1,1,col_cnt);
	while(q--){
		char ch;
		cin>>ch;
		int x=read(),y=read();
		if(ch=='A') cout<<Q(x,y)<<endl;
		else{
			update(1,dfn[x],y);
			if(!f[x]){
				w[x]=y;
				continue;
			}
			s[f[x]].erase(s[f[x]].lower_bound(w[x]));
			s[f[x]].insert(y);
			if(w[f[x]]!=*s[f[x]].begin()){
				w[f[x]]=*s[f[x]].begin();
				update(1,dfn[f[x]],w[f[x]]);
			}
			w[x]=y;
		}
	}
}

标签:cnt,int,dfn,low,圆方树,col
来源: https://www.cnblogs.com/into-qwq/p/16452284.html