• HDU 5409 CRB and Graph (边双连通+DFS)


    <题目链接>

    题目大意:

      给你一个连通的无向图,问你删除每一条边后,是否能够出现一对(u,v),使得u,v不连通,且u<v,如果有多对u,v,则输出尽量大的u,和尽量小的v。

    解题分析:
      首先要明确,因为该图是连通的无向图,所以删除的边是桥才能够使至少两点不连通。但是对于删除桥的情况,如何输出尽可能大的u和尽可能小的v呢?

      我们要知道,删除一个桥,是将整张图分成两部分,这两部分的点仍然是连通的,并且,由于题目要求u<v,且u尽可能的大,v尽可能的小,所以,我们可以推断出,u与n一定不在同一部分,并且u是它所属部分的最大值,同时由于所有点的序号是连续的,u是那一部分的最大值,所以v的值一定是u+1。所以本题的目标就已经很明确了,就是先对每个点双连通分量进行缩点,并且维护每个连通分量的最大值。最后寻找u值得时候,就直接用DFS寻找不含n点的部分的最大值即可。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 using namespace std;
      5 const int MAXN = 1e5+10;
      6 #define clr(a,b) memset(a,b,sizeof(a))
      7 struct Edge{
      8     int to,next;
      9     bool vis,bridge;
     10     Edge(){}
     11     Edge(int _to,int _next):to(_to),next(_next){vis=false;bridge=false;}
     12 }e[MAXN<<1];
     13 int times,n,m,top,bcc,tot;
     14 int dfn[MAXN],low[MAXN],pre[MAXN],head[MAXN],stk[MAXN],belong[MAXN],res[MAXN];
     15 void init(){
     16     top=times=bcc=tot=0;
     17     clr(head,-1);clr(dfn,0);clr(low,0);clr(belong,0);clr(pre,0);
     18 }
     19 void addedge(int u,int v){
     20     e[tot]=Edge(v,head[u]);head[u]=tot++;    
     21     e[tot]=Edge(u,head[v]);head[v]=tot++;
     22 }
     23 void Tarjan(int u){
     24     dfn[u]=low[u]=++times;
     25     stk[++top]=u;
     26     for(int i=head[u];i!=-1;i=e[i].next){
     27         int v=e[i].to;
     28         if(e[i].vis)continue;
     29         e[i].vis=e[i^1].vis=true;     //将正反边全部标记
     30         if(!dfn[v]){
     31             pre[v]=u;      //记录下父亲节点
     32             Tarjan(v);
     33             low[u]=min(low[u],low[v]);
     34         }
     35         else if(!belong[v])low[u]=min(low[u],dfn[v]);
     36     }
     37     if(dfn[u]==low[u]){
     38         bcc++;
     39         res[bcc]=-1;     //进行初始化
     40         belong[u]=bcc;  
     41         while(true){
     42             int v = stk[top--];
     43             belong[v]=bcc;
     44             res[bcc]=max(res[bcc],v);     //维护连通分量中的最大值
     45             if(v==u)break;
     46         }
     47     }
     48 }
     49 Edge edg[MAXN*2];
     50 int dep[MAXN],val[MAXN],head1[MAXN],tot1;
     51 void init1(){
     52     tot1=0;
     53     clr(head1,-1);clr(dep,0);
     54 }
     55 void AddEdge(int u,int v){
     56     edg[tot1]=Edge(v,head1[u]);head1[u]=tot1++;
     57     edg[tot1]=Edge(u,head1[v]);head1[v]=tot1++;
     58 }
     59 void dfs(int u,int d){    //得到以u为根的子树中编号最大的节点,并且得到每个节点的深度
     60     dep[u]=d;
     61     val[u]=res[u];
     62     for(int i=head1[u];~i;i=edg[i].next){
     63         int v=edg[i].to;
     64         if(!dep[v]){
     65             dfs(v,d+1);
     66             val[u]=max(val[u],val[v]);    //val[u]为以u为根的子树中最大的编号
     67         }
     68     }
     69 }
     70 void solve(){
     71     Tarjan(1);
     72     for(int i=0;i<tot;i++){     //遍历所有的边
     73         if(e[i].bridge)continue;
     74         int u=e[i].to,v=e[i^1].to;
     75         if(pre[v]==u&&dfn[u]<low[v]){    //如果u为v的父亲,且dfn[u]<low[v],说明u、v之间为桥
     76             e[i].bridge=e[i^1].bridge=true;  //标记该边是否为桥
     77         } 
     78     }
     79     init1();     //为缩点后重新构图进行初始化
     80     for(int i=0;i<tot;i+=2){
     81         int u=e[i].to,v=e[i^1].to;
     82         if(belong[u]!=belong[v]){
     83             AddEdge(belong[u],belong[v]);     //缩点后进行重新构图
     84         }
     85     }
     86     dfs(belong[n],1);    //以包含n的点双连通分量为树根
     87     for(int i=0;i<tot;i+=2){
     88         int u=e[i].to,v=e[i^1].to;
     89         if(e[i].bridge){   //因为肯定是两边中深度更浅的节点为根的子树中包含n节点,所以直接输出以深度更深的节点为根的最大值和这个最大值+1(这个最大值相当于是u,因为v>u,并且v要最小所以v为u+1)
     90             if(dep[belong[u]]>dep[belong[v]])printf("%d %d
    ",val[belong[u]],val[belong[u]]+1);
     91             else printf("%d %d
    ",val[belong[v]],val[belong[v]]+1);
     92         }
     93         else printf("0 0
    ");     //如果不是桥,则输出0 0 
     94     }
     95 }
     96 int main(){
     97     int T;scanf("%d",&T);
     98     while(T--){
     99         scanf("%d%d",&n,&m);
    100         init();
    101         for(int i=0,x,y;i<m;i++){
    102             scanf("%d%d",&x,&y);
    103             addedge(x,y);
    104         }
    105         solve();
    106     }
    107     return 0;
    108 }

    2018-12-04

  • 相关阅读:
    iOS 之 内存管理
    php的异步并行扩展swoole
    如何用php实现qq登陆网站
    php分页类
    php的分页代码
    一个小的投票系统
    php如何判断两个时间戳是一天
    PHP中出现Notice: Undefined index的三种解决办法
    vmvare如何安装xp虚拟机
    windows2003安装
  • 原文地址:https://www.cnblogs.com/00isok/p/10065216.html
Copyright © 2020-2023  润新知