边双连通分量

关于桥与双连通分支的定义及求法void大神给出了很详尽的介绍图的割点、桥与双连通分支


总结几条做题过程中发现的关于边双连通分量的性质:

1.将图中每个边连通分量缩为一个点后,只连端点不位于同一连通分量中的边,则将图转换成了一棵树,且树中的边都是父子边。

2.在对图进行搜索时,图中的边分为父子边和返祖边(交叉边),则一个具有n个点的边连通分量中有且仅有n-1条父子边,且可由着n-1以条边连成一棵树,剩余的边都是返祖边。

关于双连通图的构造和判定等基本问题void文章里有详细介绍,我的《连通性问题》中也有写。

poj3694 Network

题意:讯问在现有网络中添加一条边<a,b>后图中有多少条桥。

显然,若ab位于同一连通分支时不会影响桥,因此变为缩点后的树中两点之间路径上有多少条桥,由于在求双连通时记录了每个点的深度,因此不必单独求lca只需延父节点向上走,知道两个点的某个祖先相同,在寻找lca的过程中可以顺便提及出有多少条桥。

int LCA(int a, int b) {
			if(dfn[a]<dfn[b]){
				int temp=a;
				a=b;
				b=temp;
			}
			while(dfn[a]>dfn[b]){
		        if(bridge[a]){
		            sum--;
		            bridge[a]=false;
		        }
		        a=fa[a];
		    }
		    while(a!=b){
		        if(bridge[a]){sum--;bridge[a]=false;}
		        if(bridge[b]){sum--;bridge[b]=false;}
		        a=fa[a];b=fa[b];
		    }
			return sum;
		}
poj1515 Street Direction / poj1438 One-Way Traffic

题意:有m条无向边确保n个点两两可达,要求将尽可能多的双向边变为单向边,使得任意两点之间仍然可达,并给出一组方案。

解法:对于无向图的桥,肯定要保持双向,对于双连通分量分量内的点,只要确保构成果敢个环就可以,一开始想到了dfs求环路,后来想到了染色,其实构成环路的边就是搜索时的父子边和返祖边,按照原本的方向构造就可以。 

1438是1515的延伸,有些路已经是单向的了。其实很简单,只要在加边的时候加单向边,就相当于给一条无向边规定了性质,然后做相同的处理即可

public class Main{
	int maxn=1010,maxm=10010;
	class BCC {
		class node {
			int ne, be;
			int type;
			node(int be, int ne) {
				this.ne = ne;
				this.be = be;
			}
		}
		node buf[] = new node[maxm];
		int E[] = new int[maxn], len, n;
		int dfn[] = new int[maxn], low[] = new int[maxn], cnt;
		int vis[] = new int[maxn], fa[] = new int[maxn], sum;
		boolean bridge[] = new boolean[maxm];

		void init(int n) {
			this.n = n;
			Arrays.fill(E, -1);
			len = 0;
		}

		void add(int a, int b) {
			buf[len] = new node(b, E[a]);
			E[a] = len++;
			buf[len] = new node(a, E[b]);
			E[b] = len++;
		}

		void dfs(int a) {
			vis[a] = 1;
			dfn[a] = low[a] = ++cnt;
			for (int i = E[a]; i != -1; i = buf[i].ne) {
				int b = buf[i].be;
				if (vis[b] == 1 && b != fa[a]){
					buf[i].type=1;//返祖边
					low[a] = Math.min(low[a], dfn[b]);
				}
				if (vis[b] == 0) {
					buf[i].type=2;//父子边
					fa[b] = a;
					dfs(b);
					low[a] = Math.min(low[b], low[a]);
					if (low[b] > dfn[a]) {
						buf[i].type=3;//
					//	System.out.println(a+" bb "+b+" "+i);
					}
				}
			}
			vis[a] = 2;
		}

		void solve() {
			Arrays.fill(dfn, 0);
			Arrays.fill(low, 0);
			Arrays.fill(bridge, false);
			Arrays.fill(vis, 0);
			cnt = sum = 0;
			for (int i = 1; i <= n; i++)
				fa[i] = i;
			dfs(1);
		}
		void go(){
			for(int i=1;i<=n;i++)
				for(int j=E[i];j!=-1;j=buf[j].ne){
					//System.out.println(i+" "+j);
					if(buf[j].type==0)
						continue;
					int b=buf[j].be;
                    System.out.println(i+" "+b);
					if(buf[j].type==3)
						System.out.println(b+" "+i);
				}
		}
		}
	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
            new InputStreamReader(System.in)));
 
    int nextInt() throws IOException {
        in.nextToken();
        return (int) in.nval;
    }
    BCC bcc=new BCC();
    void run() throws IOException
    {
    	int cas=0;
    	while(++cas>0){
    	int n=nextInt();
    	int m=nextInt();
    	if(n==0)
    		break;
    	System.out.println(cas+"\n");
    	bcc.init(n);
    	for(int i=1;i<=m;i++)
    		bcc.add(nextInt(), nextInt());
    	bcc.solve();
    	bcc.go();
    	System.out.println("#");
    	}
    }
	public static void main(String[] args) throws IOException {
		new Main().run();
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值