• 无向图的割顶和割边模板题 P1637&&P1638


    Description

    割顶(割点或关节点):如果在图G中删去一个点v,连通分量数量增加,则称v为G的割顶。
    本题问题:给出含n个节点m条边的连通图,请计算这个图的割顶集。


    Input

    第一行包含两个整数:n和m,分别表示图的节点数量(编号为1..n)和m条边。接下来的m行,每行包含两个整数u,v,表示一条边。


    Output

    第一行输出num,表示割点数目。第二行包含num个整数,每个整数表示一个割点的编号(由小到大输出)。


    Hint

    开800000。


    Solution

    定理 1:无向连通图的DFS树中,树根结点是两个或更多子树时,它才是割顶。

    定理 2:无向连通图的DFS树中,非根结点i是割顶当且i存在一个儿j,使得j及其后代都没有返祖边连回到它的祖先(连回i不算)。

    low[i]=min(low[i],dfn[j]);如果i的儿子的时间戳比i小,那么说明i的儿子j一定存在一条返祖边,那么i就一定不是割点。
    low[i]=min(low[i],low[j]);如果j没有进去过,那么就把low[i]更新成他儿子的时间戳和他自己的最小值。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define maxn 200005
    using namespace std;
    struct Edge{
    	int u;
    	int v;
    	int next;
    	int id;
    }edge[maxn];
    int first[maxn],last[maxn],dfn[maxn],low[maxn],cut[maxn];
    int node,dfn_TimeClock,n,m,x,y,cnt;
    bool vis[maxn];
    void addedge(int u,int v,int id){
    	edge[++node]=(Edge){u,v,0,id};
    	if(first[u]==0)first[u]=node;
    	else edge[last[u]].next=node;
    	last[u]=node;
    }
    void init(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		addedge(x,y,i);
    		addedge(y,x,i);
    	}
    }
    void tarjan_(int i,int fd){
    	vis[i]=true;
    	dfn[i]=low[i]=++dfn_TimeClock;
    	int xxx=0;
    	for(int p=first[i];p;p=edge[p].next){
    		int j=edge[p].v,id=edge[p].id;
    		if(vis[j]){
    			if(dfn[j]<=dfn[i]&&id!=fd){
    				low[i]=min(low[i],dfn[j]);
    			}
    			continue;
    		}
    		xxx++;
    		tarjan_(j,id);
    		low[i]=min(low[i],low[j]);
    		if(low[j]>=dfn[i])cut[i]=1;
    	}
    	if(fd==0&&xxx==1)cut[i]=0;
    }
    int main(){
    	init();
    	for(int i=1;i<=n;i++){
    		if(!vis[i])tarjan_(i,0);
    	}
    	for(int i=1;i<=n;i++){
    		if(cut[i]==1)cnt++;
    	}
    	printf("%d
    ",cnt);
    	for(int i=1;i<=n;i++){
    		if(cut[i])printf("%d ",i);
    	}
    	return 0;
    }
    

    Description

    割边(桥):如果在图G中删去一条边e,连通分量数量增加,则称e为G的割边。
    本题问题:给出含n个节点m条边的连通图,请计算这个图的割边。


    Input

    第一行包含两个整数: n和m,分别表示图的节点数量(编号为1..n)和m条边(可能有重边或自环)。接下来的m行,每行包含两个整数u,v,表示一条边。


    Output

    第一行输出num,num表示割边数目。接下来的num行,每行表示一条割边(u,v),要求,u由小到大,如果u相等则v由小到大。


    Hint

    开800000。


    Solution

    在求割点算法中,当由i及其后代走访完毕后,如果low[i]dfn[i],则i的父边是割边(根结点没有父边)。因为当low[i]dfn[i]时,说明i及其后代必须没有连接到i的祖先的返组边存在,即i及其后代必须通过i的父边才能与其他结点连通,所以i的父边是割边。

    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #define maxn 800005
    using namespace std;
    struct Edge{
    	int u;
    	int v;
    	int next;
    	int id;
    }edge[maxn];
    struct anss{
    	int leftt;
    	int rightt;
    	friend bool operator < (anss a,anss b){
    		if(a.leftt<b.leftt)return true;
    		else if(a.leftt==b.leftt)return a.rightt<b.rightt;
    		else return false;
    	}
    }ans[maxn];
    int first[maxn],last[maxn],dfn[maxn],low[maxn];
    int node,n,m,x,y,dfn_TimeClock,cnt;
    bool vis[maxn],mark[maxn];
    void addedge(int u,int v,int id){
    	edge[++node]=(Edge){u,v,0,id};
    	if(first[u]==0)first[u]=node;
    	else edge[last[u]].next=node;
    	last[u]=node;
    }
    void init(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		addedge(x,y,i);
    		addedge(y,x,i);
    	}
    }
    void tarjan_T(int i,int fd){
    	vis[i]=true;
    	dfn[i]=low[i]=++dfn_TimeClock;
    	for(int p=first[i];p;p=edge[p].next){
    		int j=edge[p].v,id=edge[p].id;
    		if(vis[j]){
    			if(dfn[j]<=dfn[i]&&id!=fd){
    				low[i]=min(low[i],dfn[j]);
    			}
    			continue;
    		}
    		tarjan_T(j,id);
    		low[i]=min(low[i],low[j]);
    	}
    	if(low[i]==dfn[i]&&fd!=0)mark[fd]=true;
    }
    int main(){
    	init();
    	for(int i=1;i<=n;i++){
    		if(!vis[i])tarjan_T(i,0);
    	}
    	for(int i=1;i<=n;i++){
    		for(int p=first[i];p;p=edge[p].next){
    			int j=edge[p].v;
    			if(mark[edge[p].id]&&i<j){
    				ans[++cnt]=(anss){i,j};
    			}
    		}
    	}
    	sort(ans+1,ans+cnt+1);
    	printf("%d
    ",cnt);
    	for(int i=1;i<=cnt;i++){
    		printf("%d %d
    ",ans[i].leftt,ans[i].rightt);
    	}
    	return 0;
    }
    
  • 相关阅读:
    splinter webdriver API 的基本实现
    201253 线程和进程的区别
    Winform中的默认图片
    [收藏】正确使用SqlConnection对象,兼谈数据库连接池
    手机相关的基础名称
    常见排序
    SIP相关内容
    How to set the WIFI configuration
    本地化的设置和读取
    Serialize And Deserialize Binary Tree
  • 原文地址:https://www.cnblogs.com/virtual-north-Illya/p/10045338.html
Copyright © 2020-2023  润新知