zoukankan      html  css  js  c++  java
  • bzoj 2730: [HNOI2012]矿场搭建(Tarjan算法)

    刚刚学习了什么是割点,桥,点双图,边双图,以及如何求,然后就想实践一下,结果悲剧了。。。

    这道题的基本算法是用targin算法求出割点以及除去割点之后的联通块。

    1.如果这个分支中只有1个割点,那么就需要建立一个特殊点。

    2.如果有两个及以上的割点,就不需要去建立,因为无论哪个割点被爆了,都可以通过其他割点走到特殊点。

    3.如果整张图没有割点,自己就是点双连通图,那就建立两个特殊点,防止被爆。

    4.如果整张图就一个点,那么就建一个特殊点。

    唔,这是多种情况吧,具体的话在程序注释里写。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stack>
    using namespace std;
    struct h{
        int to,next;
    }edge[10000];
    int cnt,tot,m,p,t1,s1;
    int head[10000],dfn[10000],low[10000],node[10000];
    long long ans1,ans2;
    void add(int u,int v)
    {
      edge[++cnt].to=v;
      edge[cnt].next=head[u];
      head[u]=cnt;
    }
    void tarjan1(int x)
    {
        dfn[x]=low[x]=++tot;
        for (int i=head[x];i;i=edge[i].next)
        {
          if (dfn[edge[i].to]==0)
          {
              tarjan1(edge[i].to);
              low[x]=min(low[x],low[edge[i].to]);
              if (low[edge[i].to]>=dfn[x]) node[x]++;
          }
          else 
            low[x]=min(low[x],dfn[edge[i].to]);
        }
    }
    stack<int>s;
    void tarjan2(int x)//第二次我们开始记录答案
    {
      dfn[x]=low[x]=++tot;
      s.push(x);
      for (int i=head[x];i;i=edge[i].next)
      {
          if (dfn[edge[i].to]==0)//如果节点未被访问过
          {
            tarjan2(edge[i].to);//继续搜
          low[x]=min(low[x],low[edge[i].to]);
          if (low[edge[i].to]>=dfn[x])//如果这个点是一个割点
          {
              int t=0,size=0,temp=0;
              while (t!=edge[i].to)
    //开始弹出我们现在枚举的这个联通块,这里不能写t!=x,y弹出后,栈顶不一定是x.
              {
                t=s.top();
              s.pop();
              if (node[t]>=2) temp++;
    //如果是割点的话,这个联通块的割点数加1
              size++;    
    //联通块大小加1
            }
            if (node[x]>=2) temp++;
            size++;
    //下面就是四种情况分别记录答案
            if (temp==1)
            {
              ans1++; ans2=ans2*(size-1); 
            }
            else if (temp==0)
            {
              if (size==1) ans1++; 
              else
              {
                  ans1+=2; ans2=ans2*size*(size-1)/2;
              }
            }
          }    
        }
        else  low[x]=min(low[x],dfn[edge[i].to]);
      }
    }
    int main()
    {
      while (scanf("%d",&m),m>0)
       {
          tot=0;
             p++;
             cnt=0;
             memset(head,0,sizeof head);    
          memset(dfn,0,sizeof dfn);    
          memset(node,0,sizeof node);    
          int n=0; ans1=0; ans2=1; 
          for (int i=1;i<=m;i++)
          {
              scanf("%d%d",&s1,&t1);
              n=max(max(s1,t1),n);
              add(s1,t1);//链式前向星建边
              add(t1,s1);
          }
          for (int i=1;i<=n;i++)
          if (dfn[i]==0) tarjan1(i);
          else node[i]++;
    //node记录这个点会存在于几个点双中,如果只存在于一个,就说明它不是割点,如果有两个及两个以上,那就是割点,这次tarjan用于判断割点
          memset(dfn,0,sizeof dfn);
          for (int i=1;i<=n;i++)
          if (dfn[i]==0) tarjan2(i);
          printf("Case %d: %lld %lld
    ",p,ans1,ans2);
       } 
       return 0;
    }

    大概就是这样。

  • 相关阅读:
    ssh代理转发
    了解ssh代理:ssh-agent
    ssh使用密钥进行认证
    拾遗之”三元运算”与”打印奇偶行”
    MySQL 8.0二进制包安装
    awk内置函数
    awk数组详解
    awk动作总结之二
    awk动作总结之一
    [shell]批量抽取文件并重命名
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6432253.html
Copyright © 2011-2022 走看看