【BZOJ2730 || HNOI2012】矿场搭建

来源:互联网 发布:淘宝高仿手表 编辑:程序博客网 时间:2024/04/30 06:27

其实现在做得挺心酸的,4月份参加省选的时候什么都不会

特别是对关于联通分量什么题目的表示不明觉厉

现在意识到其实这和学OI的时间没有什么关系

完全是自己没有用心、没有花时间罢了,然后得花现在的时间去弥补


【题目描述】

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

题意很简单,就不解释了。


【简要分析】

首先得具备一定基础知识

不太了解的请转 -> 割点,割边,强联通分量,点双联通分量,边双联通分量

很容易知道设置救援出口的个数与割点有关系

不妨先删掉割点,剩下了一些联通块

最初想法:有几个联通块就应该设置几个

为什么这个思考方向是正确的呢? 因为据题意每次只需满足删除任意的一个点,使得此图仍满足性质……

但这仍是错的,因为当一个联通块与多个割点相连时,那么此联通块中不需要设置出口

所以转化为求删除割点后剩下的联通块中仅与一个割点相连的联通块个数

如何求方案的?根据乘法原理那就是每个满足要求的联通块的大小相乘即可

另外有个需要特判的地方

就是如果割点数为1的话

只需要选2个出口,方案显然就是C(n, 2) = n * (n - 1) / 2。


【Code】

http://ideone.com/cBXHNp

写完后发现交了BZOJ是第一版最后一名

时间都是0ms,但空间我的大一些

然后去观摩了下weixinding等人的代码

只有我苦逼的各种vector,set啊啊啊

而且……

貌似用并查集维护联通块以及其大小的就只有我这一个沙茶了

貌似用set(不可重复)去维护每个联通块周围割点个数的就只有我这一个沙茶了

但是很鲁棒哦!

#include <algorithm>#include <ctime>#include <cmath>#include <set>#include <map>#include <stack>#include <vector>#include <string>#include <cstdio>#include <climits>#include <cstring>#include <cstdlib>#include <iostream>using namespace std;#define sci stack <int>#define vci vector <int>#define vcs vector <string>#define vcd vector <double>#define vci64 vector <long long>const int maxn = 500 + 5;typedef unsigned int uint;typedef long long int64;typedef unsigned long long uint64;template <class T> inline T Sqr(const T & x) { return x * x; }template <class T> inline T Abs(const T & x) { return x > 0 ? x : -x; }template <class T> inline T Min(const T & a, const T & b) { return a < b ? a : b; }template <class T> inline T Max(const T & a, const T & b) { return a > b ? a : b; }template <class T> inline T Ksm(const T & a, const T & b, const T & m) { T _ = 1; for (; b; b >>= 1, a = a * a % m) (b & 1) ? _ = _ * a % m : 0; return _ % m; }template <class T> inline void Swap(T & a, T & b) { T _; _ = a; a = b; b = _; }int64 tot;vci g[maxn];set <int> gdn[maxn];int n, N, nt, ltknum;int ufs[maxn], fst[maxn];int dfstime, dfn[maxn], icnum;bool ic[maxn], ap[maxn];int getint(){   char ch = getchar(); int result = 0;   for (; '0' > ch || ch > '9'; ) ch = getchar();   for (; '0' <= ch && ch <= '9'; )      result = result * 10 + ch - '0', ch = getchar();   return result;}int find(int x){   int f = x, t;   for (; f != ufs[f]; f = ufs[f]);   for (; x != ufs[x]; t = ufs[x], ufs[x] = f, x = t);   return f;}int dfs(int u, int father){   int lowu = dfn[u] = ++dfstime, lowv, child = 0;   for (int i = 0, v; i < (int) g[u].size(); ++i)   {      if (!dfn[v = g[u][i]])      {         ++child, lowu = Min(lowu, lowv = dfs(v, u));         if (lowv >= dfn[u]) ic[u] = 1;      }      else if (dfn[v] < dfn[u] && v != father)         lowu = Min(lowu, dfn[v]);   }   if (father < 0 && child == 1) ic[u] = 0;   return dfn[u] = lowu;}int main(){   freopen("input.txt", "r", stdin);   freopen("output.txt", "w", stdout);   for (int T = 0; scanf("%d", &n) && (n != 0); )   {      memset(ic, 0, sizeof(ic));      memset(ap, 0, sizeof(ap));      memset(dfn, 0, sizeof(dfn));      ++T, dfstime = icnum = ltknum = N = nt = 0; tot = 1LL;      for (int i = 1; i < maxn; ++i)         g[i].clear(), gdn[i].clear();      for (int i = 1, u, v; i <= n; ++i)      {         ap[u = getint()] = ap[v = getint()] = 1;         N = Max(N, u), N = Max(N, v);         g[u].push_back(v), g[v].push_back(u);      }      for (int i = 1; i <= N; ++i) ufs[i] = i, fst[i] = 1;      for (int i = 1; i <= N; ++i) if (ap[i]) ++nt;      for (int i = 1; i <= N; ++i) if (ap[i] && !dfn[i]) dfs(i, -1);      for (int i = 1; i <= N; ++i) if (ap[i] && ic[i]) ++icnum;      if (icnum == 0)      {         printf("Case %d: 2 %I64d\n", T, (nt - 1LL) * nt / 2);         continue;      }      for (int u = 1, v; u <= N; ++u)      {         if (!ap[u] || ic[u]) continue;         for (int i = 0; i < (int) g[u].size(); ++i)         {            if (ic[v = g[u][i]]) continue;            int fu = find(u), fv = find(v);            if (fu != fv) ufs[fu] = fv, fst[fv] += fst[fu];         }      }      for (int u = 1, v, fv; u <= N; ++u)         if (ap[u] && ic[u]) for (int i = 0; i < (int) g[u].size(); ++i)               if (!ic[v = g[u][i]]) gdn[fv = find(v)].insert(u);      for (int u = 1, fu; u <= N; ++u)      {         if (!ap[u] || ic[u]) continue;         if (((fu = find(u)) == u) && ((int) gdn[fu].size() == 1))            ++ltknum, tot *= fst[fu];      }      printf("Case %d: %d %I64d\n", T, ltknum, tot);   }   return 0;}


原创粉丝点击