首页 > 其他分享 >B3610 [图论与代数结构 801] 无向图的块 题解

B3610 [图论与代数结构 801] 无向图的块 题解

时间:2023-11-04 19:44:37浏览次数:51  
标签:连通 点双 int 题解 B3610 割点 无向 low 节点

题目传送门

前言

本题解内容均摘自我的 Tarjan 学习笔记

解法

Tarjan 与无向图

无向图与割点(割顶)

  • 在一个无向图中,不存在横叉边(因为边是双向的)。

  • 一个无向图中,可能不止存在一个割点。

  • 割点(割顶):在一个无向图中,若删除节点 \(x\) 以及所有与 \(x\) 相关联的边之后,图将会被分成两个或者两个以上不相连的子图,那么称 \(x\) 为这个图的割点(割顶)。

  • 判定法则:
    当遍历到一个节点 \(x\) 时,这个点为割点的情况有两种:

    • 该节点为根节点且子节点的个数大于 \(1\)(易知此时对于 \(x\) 的任意一个子节点 \(y\) 都有 \(dfn_x<low_y\)),则删掉这个节点 \(x\) 后必将导致子节点不连通,即该节点 \(x\) 为图的一个割点。
    • 该节点不为根节点,且存在一个子节点 \(y\) 使得 \(dfn_x \le low_y\)(子节点 \(y\) 可回溯到的最早节点不早于 \(x\) 点,即子节点 \(y\) 无法回到 \(x\) 的祖先节点),则删掉这个节点 \(x\) 后必将导致 \(x\) 的父节点与 \(x\) 的子节点不连通,即该节点 \(x\) 为图的一个割点。
    • 若不存在一个子节点 \(y\) 使得 \(dfn_x \le low_y\),说明子节点 \(y\) 能绕行其他边到达比 \(x\) 更早访问的节点,\(x\) 就不是本图的割点,即环内的点割不掉。
  • 应用:如图,节点 \((0,4,5,6,7,11)\) 为割点。

无向图与双连通分量

  • 若一个无向连通图不存在割点,则称它为点双连通图。
  • 无向图中极大的点双连通子图叫点双连通分量,即 v-DCC。
  • 在一张连通的无向图中,对于两个点 \(x\) 和 \(y\),如果删去哪个点(只能删去一个,且不能删去 \(x\) 和 \(y\) 自己)都不能使它们不连通,我们就说 \(x\) 和 \(y\) 点双连通。

点双连通分量

  • 点双连通分量
    • 若某个点为孤立点,则它自己单独构成一个 v-DCC。

    • 除了孤立点之外,点双连通分量的大小至少为 \(2\)。

    • 性质

      • 点双连通分量之间以割点连接,且两个点双连通分量之间有且只有一个割点。

        • 证明:若两个点双连通分量之间共用两个点,则删除其中任意一个点,所有点依旧连通。如图

      • 每一个割点可任意属于多个点双连通分量,因此求点双连通分量时,可能包含重复的点。

      • 每一个割点都在至少两个点双连通分量中。

        • 证明:在一个非点双连通图中,删去割点后图会不连通,故割点至少连接着图的两部分。但是因为点双连通图中不存在割点,所以这两部分肯定不在同一个点双连通分量中。因此割点至少存在于两个点双连通分量中。
      • 只有一条边连通的两个点也是一个点双连通分量。如图

      • 除了上一条中的情况外,其他的点双连通分量都满足任意两点间都存在不少于两条点不重复路径。

      • 任意一个不是割点的点都只存在于一个点双连通分量中。

      • 点双连通不具有传递性,如图,\((1,3)\) 点双连通,\((1,7)\) 点双连通,但是 \((3,7)\) 不点双连通。

    • 应用:如图,存在 \((1,2,3),(3,4),(4,5,6)\) 这三个点双连通分量。

    • 算法

      • 用一个栈存点,若遍历回到 \(x\) 时,发现割点判定法则 \(dfn_x \le low_y\) 成立,则从栈中弹出节点,直到 \(y\) 被弹出。那么,刚才弹出的节点和 \(x\) 一起构成一个 v-DCC。
    • 例题

      • luogu P8435 【模板】点双连通分量
        • 事实上在求割点的同时,同时可以顺便求出点双连通分量,维护一个栈在求割点的途中若有 \(dfn_x>low_y\),则将 \((x,y)\) 入栈;而当 \(dfn_x \le low_y\) 时,将栈中所有在 \((x,y)\) 之上的边全部取出,这些边所连接的点与 \(x\) 构成了一个点双连通分量,显然割点是可以属于多个点双连通分量的。
        • 每当新搜到一个节点时,将其压入栈中。
        • 当发现 \(x\) 的子节点 \(y\) 不能通过其他方式到达 \(x\) 的祖先,但可以到达 \(x\)(即 \(dfn_x \le low_y\) 成立),则弹出栈顶元素直到 \(y\) 弹出。
        • 弹出的所有元素组成的集合 \(E\) 加上 \(x\),则为一个点双连通分量。
          #include<bits/stdc++.h>
          using namespace std;
          #define ll long long 
          #define endl '\n'
          struct node
          {
              int next,to;
          }e[4000001];
          vector<int>v_dcc[4000001];
          stack<int>s;
          int head[4000001],dfn[4000001],low[4000001],cnt=0,tot=0,ans=0;
          void add(int u,int v)
          {
              cnt++;
              e[cnt].next=head[u];
              e[cnt].to=v;
              head[u]=cnt;
          }
          void tarjan(int x,int fa)
          {
              int i,k=0;
              if(x==fa&&head[x]==0)//孤立点判定
              {
                  ans++;
                  v_dcc[ans].push_back(x);
              }
              tot++;
              dfn[x]=low[x]=tot;
              s.push(x);
              for(i=head[x];i!=0;i=e[i].next)
              {
                  if(dfn[e[i].to]==0)
                  {
                      tarjan(e[i].to,fa);
                      low[x]=min(low[x],low[e[i].to]);
                      if(low[e[i].to]>=dfn[x])
                      {
                          ans++;
                          v_dcc[ans].push_back(x);
                          while(e[i].to!=k)//弹栈时不能弹出割点,因为割点属于多个点双连通分量
                          {
                              k=s.top();
                              v_dcc[ans].push_back(k);
                              s.pop();
                          }                
                      }
                  }
                  else
                  {
                      low[x]=min(low[x],dfn[e[i].to]);
                  }
              }
          }
          int main()
          {
              int n,m,i,j,u,v;
              cin>>n>>m;
              for(i=1;i<=m;i++)
              {
                  cin>>u>>v;
                  if(u!=v)//重边会影响结果,记得特判
                  {
                      add(u,v);
                      add(v,u);
                  }
              }
              for(i=1;i<=n;i++)
              {
                  if(dfn[i]==0)//注意图可能不连通
                  {
                      tarjan(i,i);
                  }
              }
              cout<<ans<<endl;
              for(i=1;i<=ans;i++)
              {
                  cout<<v_dcc[i].size()<<" ";
                  for(j=0;j<v_dcc[i].size();j++)
                  {
                      cout<<v_dcc[i][j]<<" ";
                  }
                  cout<<endl;
              }
              return 0;
          }
          
      • luogu B3610 [图论与代数结构 801] 无向图的块
        • 此题中的块即为大小不为 \(1\) 的点双连通分量,故不需要判断孤立点了。
        • 再按字典序排序一下就行。
        #include<bits/stdc++.h>
        using namespace std;
        #define ll long long 
        #define endl '\n'
        struct node
        {
            int next,to;
        }e[4000001];
        vector<int>v_dcc[4000001];
        stack<int>s;
        int head[4000001],dfn[4000001],low[4000001],cnt=0,tot=0,ans=0;
        void add(int u,int v)
        {
            cnt++;
            e[cnt].next=head[u];
            e[cnt].to=v;
            head[u]=cnt;
        }
        void tarjan(int x,int fa)
        {
            int i,k=0;
            tot++;
            dfn[x]=low[x]=tot;
            s.push(x);
            for(i=head[x];i!=0;i=e[i].next)
            {
                if(dfn[e[i].to]==0)
                {
                    tarjan(e[i].to,fa);
                    low[x]=min(low[x],low[e[i].to]);
                    if(low[e[i].to]>=dfn[x])
                    {
                        ans++;
                        v_dcc[ans].push_back(x);
                        while(e[i].to!=k)
                        {
                            k=s.top();
                            v_dcc[ans].push_back(k);
                            s.pop();
                        }                
                    }
                }
                else
                {
                low[x]=min(low[x],dfn[e[i].to]);
                }
            }
        }
        bool cmp(vector<int> x,vector<int> y)
        {
            for(int i=0;i<min(x.size(),y.size());i++)
            {
                if(x[i]!=y[i])
                {
                    return x[i]<y[i];
                }
            }
            return x.size()<y.size();
        }
        int main()
        {
            int n,m,i,j,u,v;
            cin>>n>>m;
            for(i=1;i<=m;i++)
            {
                cin>>u>>v;
                if(u!=v)
                {
                    add(u,v);
                    add(v,u);
                }
            }
            for(i=1;i<=n;i++)
            {
                if(dfn[i]==0)
                {
                    tarjan(i,i);
                }
            }
            cout<<ans<<endl;
            for(i=1;i<=ans;i++)
            {
                sort(v_dcc[i].begin(),v_dcc[i].end());
            }
            sort(v_dcc+1,v_dcc+1+ans,cmp);
            for(i=1;i<=ans;i++)
            {
                for(j=0;j<v_dcc[i].size();j++)
                {
                    cout<<v_dcc[i][j]<<" ";
                }
                cout<<endl;
            }
            return 0;
        }
        

标签:连通,点双,int,题解,B3610,割点,无向,low,节点
From: https://www.cnblogs.com/The-Shadow-Dragon/p/17809713.html

相关文章

  • NEFU OJ Problem1356 帽儿山奇怪的棋盘 题解
    帽儿山奇怪的棋盘题目:TimeLimit:1000ms|MemoryLimit:65535KDescription军哥来到了帽儿山,发现有两位神人在顶上对弈。棋盘长成下图的模样:每个点都有一个编号:由上到下,由左到右,依次编号为1、2……12。两位神人轮流博弈,每一轮操作的一方可以取走一个棋子,或者取走相邻的两......
  • T392582 我有抑郁症【题解】
    题目描述要求有多少个序列满足:令\(v=1\simn\)从\(v\)号点开始,走到\(p_v\),…,最后走回\(v\)记录每个点被走到的次数(起点算,终点不算,反正只算一次)\(i\)号点走到的次数恰好是\(i\)答案对\(998,244,353\)取模Solution首先,一个点有一个出边,这个图为什么一定可以走回......
  • 【洛谷 P1085】[NOIP2004 普及组] 不高兴的津津 题解(打擂台法)
    [NOIP2004普及组]不高兴的津津题目描述津津上初中了。妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班。另外每周妈妈还会送她去学习朗诵、舞蹈和钢琴。但是津津如果一天上课超过八个小时就会不高兴,而且上得越久就会越不高兴。假设津津不会因......
  • [题解]AT_abc222_f [ABC222F] Expensive Expense
    板子题,模拟赛场切了。思路线段树换根板子题。因为需要求每一个点的答案,所以定义\(dp_i\)表示以\(i\)为根的最长距离。考虑将一个点\(v\)转化为根,树的形态会发生什么变化(假设\(v\)的父亲节点是\(u\))。发现在\(v\)子树中的节点,距离都会减少\(w_{u\tov}\),其它节点......
  • 跨域问题解决办法
    跨域问题及解决#xss:跨站脚本攻击,cors:跨域资源共享,csrf:跨站请求伪造#1同源策略:请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.#2CORS:跨域资源共享,允许不同的域来我的服务器拿数据#3CORS请求分成两类:简单请求(simplerequest)和非简单请求(no......
  • CF1866D Digital Wallet 题解
    Problem-1866D-CodeforcesDigitalWallet-洛谷不妨为选数钦定一个顺序:不同行之间无影响,列从左到右取一定不劣。设计状态:设\(dp_{i,j}\)表示前\(i\)次操作操作到第\(j\)列的最大答案转移:因为对于同一列不互相影响,因此枚举这一列取\(c\)个数,显然取这一列数......
  • 数据库问题解析
    1、表连接表连接(JOIN)是在多个表中间通过⼀定的连接条件,使表之间发⽣关联进⽽能从多个表之间获取数据。2、3、表联合union:对两个结果集进⾏并集操作,不包括重复⾏unionall:对两个结果集进⾏并集操作,包括重复⾏注意事项:①每条SELECT语句必须拥有相同数量的列;②每条SELE......
  • P9817 题解
    这里提供一个非常暴力但是期望复杂度很低的算法。不难想到要么就是全部放\(1\),要么就是取出一个最大的质数,然后对于剩下的部分继续按照这样的策略求答案。因为质数间隔不大,然后暴力判断质数复杂度是\(O(\sqrtn)\)的,再加上IOI的buff,我们可以直接考虑从大到小枚举质数,因为......
  • [ARC104F] Visibility Sequence 题解
    题意对于一个长度为\(N\)的序列\(H\),可以通过如下方式构造一个序列\(P\):若存在\(j\in\left[1,i\right)\),使得\(H_j>H_i\),则\(P_i=\max\limits_{j\in\left[1,i\right)\landH_j>H_i}j\),否则\(P_i=-1\)。给定一个长度为\(N\)的序列\(X\),求所有满足如......
  • CF1866M Mighty Rock Tower 题解
    Problem-1866M-CodeforcesMightyRockTower-洛谷先考虑一个\(O(n^2)\)的dp设计状态:\(dp_i\)表示搭\(i\)层的期望转移:\(dp_i=dp_{i-1}\times(1-P_i)+\sum\limits_{j=i}^{n-1}dp_j\timesP_{j+1}^{j-i+1}\times(1-P_{j+1})\),显然是有后效性的,但我们展开......