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;
    }

    大概就是这样。

  • 相关阅读:
    shell 操作钉钉机器人实现告警提醒
    谨慎 mongodb 关于数字操作可能导致类型及精度变化
    数据库如何应对保障大促活动
    SQL Server Alwayson架构下 服务器 各虚拟IP漂移监控告警的功能实现 -1(服务器视角)
    通过 Telegraf + InfluxDB + Grafana 快速搭建监控体系的详细步骤
    MySQL数据库Inception工具学习与测试 笔记
    MongoDB 中数据的替换方法实现 --类Replace()函数功能
    MongoDB 中的【加减乘除】运算
    MySQL索引设计需要考虑哪些因素?
    关于SQL Server 数据库归档的一些思考和改进
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6432253.html
Copyright © 2011-2022 走看看