zoukankan      html  css  js  c++  java
  • 【暖*墟】 #图论# 割点

    【例题1】洛谷p3225 矿场搭建

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    /*【p3225】矿场搭建 */
    
    //【标签】数学统计 + 分情况讨论 + 割点 + 无向图双连通分量
    
    /* 割点:在一个【无向图】中,如果有一个顶点集合,
            删除这个顶点集合以及这个集合中所有顶点相关联的边以后,
            图的连通分量增多,就称这个点集为割点【集合】。  */
    
    // 双连通分量:无向图中的强连通分量,去掉任意一个节点都不会改变连通性
    
    /*【tarjan算法求割点】
    1.判断根节点:计算其子树数量,如果有2棵即以上的子树,就是割点。
    2.回顾一下low[i]的定义:u及其子树中的点,能够连向到的dfn最小的点的dfn值。
    2.对于边(u,v),如果low[v]>=dfn[u],那么low[v]没有返祖边,此时u就是割点。*/
    
    /*【思路】统计每个双连通分量里的割点的个数,并分情况讨论。
    1.若该连通分量里割点>=2,可以通过其他割点跑到其他的双连通分量里。
    2.若该连通分量里割点=1,所以要在任意一个非割点处建一个出口。
    3.若该连通分量里割点=0,说明它与外界完全不连通,需要建两个出口以防万一。*/
    
    void reads(int &x){ //读入优化(正负整数)
        int fa=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')fa=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
        x*=fa; //正负号
    }
    
    const int N=519;
    
    int n,m,tot=0,head[N],dfn[N],low[N],cut[N],vis[N],root;
    
    int dfn_=0,sum,cnt,cut_num; //连通块数sum,当前连通块节点数、割点数
    
    int kase=0; ll ans1,ans2; //选点数,方案数
    
    struct node{ int ver,nextt; }e[N*2];
    
    void add(int u,int v){ e[++tot].ver=v,e[tot].nextt=head[u],head[u]=tot; }
    
    void tarjan(int u,int fa){ //【tarjan求割点】
        
        dfn[u]=low[u]=++dfn_; int child=0;
        //fa是子树的根节点,如果儿子数>=2则是割点
       
        for(int i=head[u];i;i=e[i].nextt){
          if(!dfn[e[i].ver]){ 
            tarjan(e[i].ver,u),low[u]=min(low[u],low[e[i].ver]);
            if(low[e[i].ver]>=dfn[u]&&u!=root) cut[u]=true;
            if(u==root) child++; //root节点的儿子数++
          } low[u]=min(low[u],dfn[e[i].ver]);
        } if(child>=2&&u==root) cut[u]=true;
    }
    
    void dfs(int u){ //遍历每个连通块
        vis[u]=sum; cnt++; //节点数cnt++
        for(int i=head[u];i;i=e[i].nextt){
            if(cut[e[i].ver]&&vis[e[i].ver]!=sum) 
                cut_num++,vis[e[i].ver]=sum; //e[i].ver是割点
            if(!vis[e[i].ver]) dfs(e[i].ver); //非割点
        }
    }
    
    void init(){
        memset(head,0,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(cut,0,sizeof(cut));
        memset(vis,0,sizeof(vis));
        dfn_=n=tot=sum=ans1=0,ans2=1;
    }
    
    int main(/*hs_love_wjy*/){
        while(scanf("%d",&m)==1&&m){
            init(); //日常清零
            for(int i=1,u,v;i<=m;i++){
                reads(u),reads(v),n=max(n,max(u,v));
                add(u,v),add(v,u); //↑↑记录点的总个数n
            } for(int i=1;i<=n;i++) if(!dfn[i]) root=i,tarjan(i,0);
            for(int i=1;i<=n;i++) if(!vis[i]&&!cut[i]){
                sum++; cnt=cut_num=0; dfs(i); //到达一个新的连通块
                if(!cut_num) ans1+=2,ans2*=cnt*(cnt-1)/2; //建两个
                if(cut_num==1) ans1++,ans2*=cnt; //建一个
            } printf("Case %d: %lld %lld
    ",++kase,ans1,ans2);
        }
    }

    【例题2】洛谷p3469 BLO-Blockade

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<string>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<map>
    using namespace std;
    typedef long long ll;
    
    /*【p3469】BLO-Blockade */
    
    /* 割点:在一个【无向图】中,如果有一个顶点集合,
            删除这个顶点集合以及这个集合中所有顶点相关联的边以后,
            图的连通分量增多,就称这个点集为割点【集合】。  */
    
    /*【tarjan算法求割点】
    1.判断根节点:计算其子树数量,如果有2棵即以上的子树,就是割点。
    2.回顾一下low[i]的定义:u及其子树中的点,能够连向到的dfn最小的点的dfn值。
    2.对于边(u,v),如果low[v]>=dfn[u],那么low[v]没有返祖边,此时u就是割点。*/
    
    void reads(int &x){ //读入优化(正负整数)
        int fa=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')fa=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
        x*=fa; //正负号
    }
    
    const int N=1000019; ll ans[N];
     
    int n,m,tot=0,head[N],dfn[N],low[N],cut[N],siz[N],dfn_=0;
    
    struct node{ int ver,nextt; }e[N*2];
    
    void add(int u,int v){ e[++tot].ver=v,e[tot].nextt=head[u],head[u]=tot; }
    
    void tarjan(int u){ //【tarjan求割点】
        dfn[u]=low[u]=++dfn_; siz[u]=1;
        int son=0,sum=0; //对于每个新节点都要清零
        for(int i=head[u];i;i=e[i].nextt){
          if(!dfn[e[i].ver]){
            tarjan(e[i].ver),siz[u]+=siz[e[i].ver];
            low[u]=min(low[u],low[e[i].ver]);
            if(low[e[i].ver]>=dfn[u]){
              ans[u]+=(ll)siz[e[i].ver]*(n-siz[e[i].ver]);
              sum+=siz[e[i].ver],son++;
              if(u!=1||son>1) cut[u]=true; }
          } else low[u]=min(low[u],dfn[e[i].ver]);
        } if(!cut[u]) ans[u]=2*(n-1);
          else ans[u]+=(ll)(n-sum-1)*(sum+1)+(n-1);
    }
    
    int main(/*hs_love_wjy*/){
        reads(n),reads(m);
        for(int i=1,u,v;i<=m;i++)
            reads(u),reads(v),add(u,v),add(v,u);
        tarjan(1); //初始完全联通
        for(int i=1;i<=n;i++) printf("%lld
    ",ans[i]);
    }

                                       ——时间划过风的轨迹,那个少年,还在等你。

  • 相关阅读:
    方法重载与方法重写的概念和区别(转载)
    sql exist 和not exist(转载)
    SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON 什么意思 sql server 2005 2008
    sql中的isnull
    sql中unique和distinct
    SQLServer中的Merge使用
    SQL Server系统表sysobjects介绍与使用
    sql select as
    存储过程SET XACT_ABORT ON
    SQL Server之存储过程基础知识
  • 原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10329328.html
Copyright © 2011-2022 走看看