【tarjan求割顶】BZOJ2730-[HNOI2012]矿场搭建

【题目大意】

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

【思路】

可以得出这样的结论:

(1)如果一个点是割点,那么在它上面建救援出口是没有意义的

(2)对于出去割点后的连通块,如果它和两个及以上的割点相连,这不需要建救援出口;否则要建一个救援出口,在连通块里选任意一个即可,最终方案数为这些连通块大小的乘积。

(3)如果只有一个联通块,那么至少建两个救援出口,最终方案书为(size)*(size-1)/2。

所以这样做:

首先用tarjan求出割点并记录,然后跑dfs求出除去割点后的连通块的个数和大小,再依据上述结论输出答案。

【错误点】

数组从0开始和从1开始搞错了一次..判断连通块周围割点的个数的时候弄错了…具体见注释

一开始忘记清空vector,后来发现用clear清空的话会RE。请教之后才知道vector清空的正确方式长成这个样子:vector<int>().swap(E[i])

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 using namespace std;
  7 const int MAXN=10000+50;
  8 vector<int> E[MAXN];
  9 int kase=0;
 10 int n,m;//隧道的数量和矿井的数量
 11 int dfn[MAXN],low[MAXN],vis[MAXN];
 12 int cut[MAXN],cnt;//记录每一个点是否是割点以及连通块的总数
 13 int nearcut[MAXN],size[MAXN];//当前连通块可以到达的割点数量&每个连通块的大小
 14 int t;
 15 int viscut[MAXN];
 16
 17 int tarjan(int isrt,int u)
 18 {
 19     vis[u]=1;
 20     dfn[u]=low[u]=++t;
 21     int sonnum=0;
 22     for (int i=0;i<E[u].size();i++)
 23     {
 24         int son=E[u][i];
 25         if (!vis[son])
 26         {
 27             sonnum++;
 28             tarjan(0,son);
 29             low[u]=min(low[u],low[son]);
 30             if (isrt && sonnum>=2) cut[u]=1;//如果是根节点,且孩子的数量大于等于两个,则是割点
 31             if (!isrt && dfn[u]<=low[son]) cut[u]=1; //如果不是根节点,并且孩子的low大于等于当前的dfn,即没有走到过u之前,则是割点
 32         }
 33         else
 34             low[u]=min(low[u],dfn[son]);
 35     }
 36 }
 37
 38 void dfs(int u)
 39 {
 40     vis[u]=1;
 41     size[cnt]++;
 42     for (int i=0;i<E[u].size();i++)
 43     {
 44         int son=E[u][i];
 45         if (!vis[son])
 46             if (cut[son])
 47             {
 48                 if (viscut[son]!=cnt)
 49                 {
 50                     //错误点:我一开始直接cnt++了,但是这样是不对的,会导致同一个割点被多次累加
 51                     nearcut[cnt]++;
 52                     viscut[son]=cnt;
 53                 }
 54             }
 55             else dfs(son);
 56     }
 57 }
 58
 59 void init()
 60 {
 61     for (int i=0;i<MAXN;i++) vector<int>().swap(E[i]);
 62     m=0;
 63     for (int i=0;i<n;i++)
 64     {
 65         int u,v;
 66         scanf("%d%d",&u,&v);
 67         E[u].push_back(v);
 68         E[v].push_back(u);
 69         m=max(m,max(u,v));//记下总共的矿井数量
 70     }
 71 }
 72
 73 void solve()
 74 {
 75     memset(vis,0,sizeof(vis));
 76     memset(cut,0,sizeof(cut));
 77     memset(size,0,sizeof(size));
 78     memset(viscut,0,sizeof(viscut));
 79     t=0;
 80     for (int i=1;i<=m;i++)//错误点:数组都要从1开始
 81     {
 82         if (vis[i]==0)
 83             tarjan(1,i);//依次找出所有的割点
 84     }
 85     memset(vis,0,sizeof(vis));
 86     cnt=0;
 87     for (int i=1;i<=m;i++)
 88         if (!cut[i] && !vis[i])
 89         {
 90             cnt++;
 91             nearcut[cnt]=0;
 92             dfs(i);
 93         }
 94 }
 95
 96
 97 void output()
 98 {
 99     printf("Case %d: ",kase);
100     if (cnt==1)
101     {
102         printf("2 %lld\n",(long long)m*(m-1)/2);
103         return;
104     }
105
106     long long tot=0;
107     long long ans=1;
108     if (cnt>1)
109     {
110         for (int i=1;i<=cnt;i++)
111         {
112             if (nearcut[i]==1)
113             {
114                 tot++;
115                 ans*=size[i];
116             }
117         }
118         printf("%lld %lld\n",tot,ans);
119     }
120 }
121
122 int main()
123 {
124     for(;;)
125     {
126         scanf("%d",&n);
127         if (n==0) break;
128         kase++;
129         init();
130         solve();
131         output();
132     }
133     return 0;
134 } 
时间: 2024-10-24 18:55:02

【tarjan求割顶】BZOJ2730-[HNOI2012]矿场搭建的相关文章

【双连通分量】Bzoj2730 HNOI2012 矿场搭建

Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. Sulotion 坍塌的不是割顶并不影响,建在割顶也没有意义(拆割顶) 考虑每一个双连通分量,如果只连一个割顶必须内部建一个(随便选一个),连两个以上可以

[BZOJ2730][HNOI2012]矿场搭建(求割点)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2730 分析: 如果坍塌的点不是割点,那没什么影响,主要考虑坍塌的点是割点的情况. 显然我们可以先求出所有割点把整个图分成若干个连通块 如果一个联通块只与一个割点相连,说明我们必须在这个连通块中选一个点作为出口 如果一个联通块与两个以上的割点相连,那么就可以不在这些点取一个作为出口 方案数就是要取点的联通块的点的个数的乘积 注意没有割点的情况要放2个点

bzoj2730: [HNOI2012]矿场搭建

Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T

【BZOJ-2730】矿场搭建 Tarjan 双连通分量

2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1602  Solved: 751[Submit][Status][Discuss] Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用

BZOJ 2730:[HNOI2012]矿场搭建(割点+连通块)

[HNOI2012]矿场搭建 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数.Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空

洛谷——P3225 [HNOI2012]矿场搭建

P3225 [HNOI2012]矿场搭建 题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口. 请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. 输入输出格式 输入格式: 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N<=500),表示工地的隧道数,接下来的

[HNOI2012]矿场搭建 题解

[HNOI2012]矿场搭建 时间限制: 1 Sec  内存限制: 128 MB 题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数. 输入 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的

UVA - 315 Network 和 UVA - 10199 (求割顶)

链接 :  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20837 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=21278 求割顶的裸题. UVA - 315 #include <algorithm> #include <iostream> #include <sstream> #include <cstrin

cogs——1348. [HNOI2012]矿场搭建

1348. [HNOI2012]矿场搭建 ★★★   输入文件:bzoj_2730.in   输出文件:bzoj_2730.out   简单对比时间限制:1 s   内存限制:128 MB [题目描述] 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少