zoukankan      html  css  js  c++  java
  • 【HNOI2012】矿场搭建

    题面

    https://www.luogu.org/problem/P3225

    题解

    首先先对割点这个东西有一个形象的理解。

    一个割点就是两个或多个点双的交,所以不能把它理解成“缩点后形成的树”上的边。

    也可以理解成是一个点双的边界且不能是图的边界,所以我认为割点并不是很优美。

    两个点双之间,最多只有一个割点(要不然就不叫割点了)

    割点和桥的对比:

    点双要求严格,所以点双都较小,所以割点好找。($low[y]>=dfn[x]$)(说一句,在此过程中,父边能不能更新$low[x]$是不影响结果的)

    边双要求不严格,所以边双比较大,所以割边难找。($low[y]>dfn[x]$)(此过程中,显然不能更新)

    再说割点的求法(多年不写我早已遗忘)

    当$low[y]>=dfn[x]$时,应该弹栈,直到弹到$y$,再把$x$“算到”这个点双里面(如果弹栈到$x$,先搜的下面的已经判定和$x$形成一个点双的部分,就被划归为这个点双里了,但其实,这些部分应该和$x$和$x$上面的点是一个点双里的)

    这样求点双是没有要特判的部分的。

    但是求割点的时候,由于不能是边界上的点,所以当$x$是根时,要多维护一个$rs$,记录$x$的不相交的儿子总共有多少个,如果$rs<=1$,就出现了乌龙,$x$不是割点,因为$x$没有上面的部分。

    回到我们这题

    我们把原图中的一个点双建一个点,一个割点建一个点,一个割点向它所在的点双们连边,这样就形成了一颗树,并且割点所代表的点,他们的度数不为$1$,如果看做无根树 ,他们一定不在边界上,在边界上的点一定是点双代表的点。

    我们只考虑在非割点上建出口的情况,对于度为$1$的代表点双的点,我们必须在其中任意一个非割点建一个出口。

    对于度$>=2$的代表点双的点,我们可以通过它走到其他叶子点上,所以不用建。

    还有一种情况是特殊考虑的,就是整个联通块缩成一个孤立的点,也就是没有割点,那我们任选两个建出口(一个塌了走另外一个)

    那如果考虑,可以在割点上建出口,答案会不会更优呢?

    本来度数就$>=2$的点不需要建,我们不考虑。

    叶子点双是必须建的,如果我们在割点上建,可以一个当多个使用啊(滑稽),但其实这样是错误的。因为如果一个叶子点双,只在割点上建,那么割点塌了就没的逃了。。。。。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #define LL long long 
    #define N 550
    #define ri register int
    using namespace std;
    
    int dfn[N],low[N],cut[N];
    int n,num,ctc,cc,T;
    vector<int> to[N],c1[N],c2[N];
    stack<int> s;
    LL ans1,ans2;
    
    void add_edge(int u,int v) {
      to[u].push_back(v); to[v].push_back(u);
    }
    
    void tarjan(int x,int ff) {
      dfn[x]=low[x]=++cc;
      s.push(x);
      
      int rs=0;
      for (ri i=0;i<to[x].size();i++) {
        int y=to[x][i];
        if (!dfn[y]) {
          tarjan(y,x);
          low[x]=min(low[x],low[y]);
          if (low[y]>=dfn[x]) {
            rs++;
            cut[x]=1;
            int t;
            num=ctc=0;
            while (1) {
              t=s.top(); s.pop();
              if (cut[t]) ++ctc; else ++num;
              if (t==y) break;
            }
            if (cut[x]) ++ctc; else ++num;
            c1[x].push_back(ctc); c2[x].push_back(num);
          }
        }
        else low[x]=min(low[x],dfn[y]);
      }
      
      if (x==ff && rs<=1) {
        for (ri i=0;i<c1[x].size();i++) c1[x][i]--,c2[x][i]++;
      }
      
      for (ri i=0;i<c1[x].size();i++) {
        if (c1[x][i]==0) {
          ans1+=2;
          ans2*=((c2[x][i]-1)*c2[x][i])/2;
        }
        else if (c1[x][i]==1) {
          ans1++;
          ans2*=c2[x][i];
        }
      }
    }
    
    int main() {
      freopen("mine1.in","r",stdin);
      int m,u,v;
      while (scanf("%d",&m)==1 && m) {
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(cut,0,sizeof(cut));
        for (ri i=1;i<=n;i++) to[i].clear(),c1[i].clear(),c2[i].clear();
        while (!s.empty()) s.pop();
        n=0; cc=0;
        ans1=0; ans2=1;
        for (ri i=1;i<=m;i++) {
          scanf("%d %d",&u,&v);
          add_edge(u,v);
          n=max(n,u);
          n=max(n,v);
        }
        for (ri i=1;i<=n;i++) if (!dfn[i]) tarjan(i,i);
        printf("Case %d: %lld %lld
    ",++T,ans1,ans2);
      }
      return 0;
    }
  • 相关阅读:
    jQuery学习之------对标签属性的操作
    jQuery学习之------选择器
    PHP读取mysql中的数据
    sql server 数据库创建链接服务器访问另外一个sql server 数据库
    SQLServer使用链接服务器远程查询
    解决SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问的方法
    Delphi XE10百集视频教程计划
    Windows 版本的iTunes 修改iPhone的备份路径
    Centos7 下mysql 密码重置
    Windows server 2012文件服务器配置
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11344735.html
Copyright © 2011-2022 走看看