点双连通分量及圆方树学习笔记

点双连通分量:

点双(点双连通分量)基于无向图,一个分量满足为点双当且仅当任意两个点之间都可找到两条点不重复的路径,可以理解为若干个有边相交的环。
有一个性质为点双内任意两点的点不重复路径都在点双内。

缩点双:

用tarjan求割顶的办法来求点双,即在求割顶的过程中用一个栈来记录下经过的点,当找到一个点为割顶的时候就弹栈,直到把它目前指向的那个结点给弹完。有一点需要注意的是每一个割顶可能存在于多个点双内,这就是为什么之前只弹到它之前指向的结点,应为它自己可能还在很多个点双内,最后单独记录一下目前的割顶就好了。

圆方树:

圆方树是在缩点双的基础下形成的一种算法,即每一个点双单独开一个方点,方点作为中心和之前的每一个点双内的点(圆点)连边。这里比较特殊的是两个点一条边也算是一个点双,中间用一个方点来连接,对最后的树上处理并没有什么影响。
这样一条图上的路径就变成了树上的路径,经过方点就表示经过了这个方点所表示的点双。
例题:求一个图中1号点到每一个点的点不重复路径中的点权最小值。
做法:直接建立圆方树后跑一遍dfs即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=2e5+10;
const int maxm=2e5+10;
const int maxq=1e6+10;
const int inf=0x7f7f7f7f;
int n,m,q,query[maxq],w[maxn],tot,ans[maxn];
vector<int>to1[maxn],to2[maxn];
int low[maxn],dfn[maxn],cnt_dfn;
stack<int>s;

void tarjan(int u,int f){
	dfn[u]=low[u]=++cnt_dfn; s.push(u);
	int siz=to1[u].size()-1;
	REP(i,0,siz){
		int v=to1[u][i];
		if(v==f)continue;
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				++tot;
				int Minval=inf;
				while(s.size()){
					int node=s.top();
					to2[tot+n].push_back(node);
					to2[node].push_back(tot+n);
					Minval=min(Minval,w[node]);
					s.pop(); if(node==v)break;
				}
				to2[tot+n].push_back(u);
				to2[u].push_back(tot+n);
				Minval=min(Minval,w[u]);
				w[n+tot]=Minval;
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

void solve(int u,int f){
	ans[u]=min(ans[f],w[u]);
	int siz=to2[u].size()-1;
	REP(i,0,siz){
		int v=to2[u][i];
		if(v==f)continue;
		solve(v,u);
	}
}

void init(){
	read(n); read(m); read(q);
	int u,v;
	REP(i,1,m){
		read(u); read(v);
		to1[-u].push_back(-v);
		to1[-v].push_back(-u);
	}
	REP(i,1,n)read(w[i]);
	REP(i,1,q)read(query[i]);
	tarjan(1,0);
	REP(i,0,n+tot)ans[i]=inf;
}

int main(){
	File();
	init();
	solve(1,0);
	REP(i,1,q)printf("%d\n",ans[query[i]]);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值