圆方树
作者:互联网
目录
圆方树
前言
众所周知,树往往有着许多非常好的性质
圆方树就是一种把一个图变成一棵树的方式
定义
我们一般在无向图上使用圆方树
先介绍点双连通分量
一个点双连通图的定义:图中任意两点之间都至少有两条不同的路径可互相抵达
一个点双连通分量是一个极大点双连通图
在圆方树中,每个原图中的点对应一个圆点,每个点双对应方点,每个点(圆点)和其对应的点双(方点)连边
上图就分别展示了原图,点双以及对应的圆方树
显然,圆点不和圆点连,方点不和方点连
我们可以在方点处维护有关点双内多条路径的信息
如何建树
首先我们需要跑一遍 \(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
题意:
给定一张图(保证连通),每个点有点权。
现在有两种操作:
-
C a w
:把 \(a\) 的点权改为 \(w\) -
A a b
:询问从 \(a\)到 \(b\) 的所有简单路径(不经过重复点)中,点权最小的点的点权。
思路:
在图上搞树剖的操作?
考虑先把图变成一棵树
采用圆方树,然后搞树剖
容易想到把每个方点的权值设为周围所有圆点的最小的权值,搞个 \(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