这个题一开始想法偏了,看了题解才搞明白的。
考虑对于每个vdcc(点双联通分量),如果不与任何一个割点相连,就至少要在块内预留两个逃生出口作为双保险。而如果与且仅与某一个割点相连的话,只要在块内建造一个点,就可以同时防范割点塌掉/逃生点塌掉的情况。而只要与两个及以上的割点相连,无论哪边塌掉,都可以从另一边逃生。
至于建造方法数,需要用到组合和乘法原理来求解。
一种思路是,先跑出所有的割点,然后搜索每个联通块与哪几个割点相连。这就需要记录每一个dcc内除割点外有几个点,用组合数算出建造1/2个出口分别有多少种方式。
另外想到一种方法:我们可以不搜索,而在求割点时把vdcc的元素记录下来,最后遍历每个vdcc找割点即可。灵感来自于vdcc缩点,省去了搜索时防重复计算的一大堆特判QwQ。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cctype>
- #include <vector>
- #include <stack>
- #define maxn 510
- using namespace std;
- int n, cas, N;
- template <typename T>
- void read(T &x) {
- x = 0;
- int f = 1;
- char ch = getchar();
- while (!isdigit(ch)) {
- if (ch == '-') f = -1;
- ch = getchar();
- }
- while (isdigit(ch))
- x = x * 10 + (ch ^ 48),
- ch = getchar();
- x *= f;
- }
- struct E {
- int to, nxt;
- } edge[maxn << 1];
- int head[maxn], top = 1;
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- int dfn[maxn], low[maxn], timer, root, cnt;
- int weight, Cut, vis[maxn], vdcc_cnt;
- bool cut[maxn];
- void dfs(int u) {
- dfn[u] = low[u] = ++timer;
- /* if (u == root && head[u] == 0) {
- cnt_v[++cnt] = 1;
- return;
- } ¹ÂÁ¢µã£ºÕâÊDz»¿ÉÄܵÄÇé¿ö*/
- // sta.push(u);
- int flag = 0;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (!dfn[v]) {
- dfs(v);
- low[u] = min(low[u], low[v]);
- if (dfn[u] == low[v]) {
- ++flag;
- if (u != root || flag > 1) {
- cut[u] = true;
- }
- }
- } else
- low[u] = min(low[u], dfn[v]);
- }
- }
- void tarjan() {
- for (int i = 1; i <= N; ++i)
- if (!dfn[i])
- root = i, dfs(i);
- }
- void judge(int u) {
- vis[u] = vdcc_cnt;
- ++weight;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (cut[v] && vis[v] != vdcc_cnt) {
- ++Cut;
- vis[v] = vdcc_cnt;
- }
- if (!vis[v])
- judge(v);
- }
- return;
- }
- void solve() {
- int ans1 = 0;
- unsigned long long ans2 = 1;
- for (int i = 1; i <= N; i++)
- if (!vis[i] && !cut[i]) {
- weight = Cut = 0;
- ++vdcc_cnt;
- judge(i);
- if (Cut == 0) {
- ans1 += 2;
- ans2 *= weight * (weight - 1) / 2;
- } else if (Cut == 1) {
- ans1 += 1;
- ans2 *= weight;
- }
- }
- printf("Case %d: %d %lld ", cas, ans1, ans2);
- return;
- }
- void init() {
- top = 1, cnt = 0, timer = 0;
- vdcc_cnt = 0;
- N = 0;
- memset(head, 0, sizeof(head));
- memset(dfn, 0, sizeof(dfn));
- memset(low, 0, sizeof(low));
- memset(vis, 0, sizeof(vis));
- memset(cut, 0, sizeof(cut));
- // for (int i = 1; i <= 1000; ++i)
- // edge[i] = (E) {0, 0};
- }
- int main() {
- while (1) {
- read(n);
- ++cas;
- if (!n) return 0;
- init();
- int u, v;
- for (int i = 1; i <= n; ++i) {
- read(u), read(v);
- insert(u, v), insert(v, u);
- N = max(max(u, v), N);
- }
- tarjan();
- solve();
- }
- return 0;
- }
- /////////第二种
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cctype>
- #include <vector>
- #include <stack>
- #define maxn 510
- using namespace std;
- int n, cas, N;
- template <typename T>
- void read(T &x) {
- x = 0;
- int f = 1;
- char ch = getchar();
- while (!isdigit(ch)) {
- if (ch == '-') f = -1;
- ch = getchar();
- }
- while (isdigit(ch))
- x = x * 10 + (ch ^ 48),
- ch = getchar();
- x *= f;
- }
- struct E {
- int to, nxt;
- } edge[maxn << 1];
- int head[maxn], top = 1;
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- int dfn[maxn], low[maxn], timer, root, cnt;
- vector<int> vdcc[maxn];
- stack<int> sta;
- bool cut[maxn];
- void dfs(int u) {
- dfn[u] = low[u] = ++timer;
- /* if (u == root && head[u] == 0) {
- cnt_v[++cnt] = 1;
- return;
- } 不可能有孤立点*/
- sta.push(u);
- int flag = 0;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (!dfn[v]) {
- dfs(v);
- low[u] = min(low[u], low[v]);
- if (dfn[u] == low[v]) {
- ++flag;
- if (u != root || flag > 1)
- cut[u] = true;
- ++cnt;
- int x;
- do {
- x = sta.top();
- sta.pop();
- vdcc[cnt].push_back(x);
- } while (x != v);
- vdcc[cnt].push_back(u);
- }
- } else
- low[u] = min(low[u], dfn[v]);
- }
- }
- void tarjan() {
- for (int i = 1; i <= N; ++i)
- if (!dfn[i])
- root = i, dfs(i);
- }
- void solve() {
- int ans1 = 0;
- unsigned long long ans2 = 1;
- for (int i = 1; i <= cnt; ++i) {
- int Cut = 0, w = vdcc[i].size();
- for (int j = 0; j < w; ++j)
- if (cut[vdcc[i][j]])
- ++Cut;
- if (Cut == 0)
- ans1 += 2, ans2 *= w * (w - 1) / 2;
- if (Cut == 1)
- ans1 += 1, ans2 *= w - Cut;
- }
- printf("Case %d: %d %lld ", cas, ans1, ans2);
- return;
- }
- void init() {
- for (int i = 1; i <= cnt; ++i)
- vdcc[i].clear();
- top = 1, cnt = 0, timer = 0;
- N = 0;
- memset(head, 0, sizeof(head));
- memset(dfn, 0, sizeof(dfn));
- memset(low, 0, sizeof(low));
- memset(cut, 0, sizeof(cut));
- while (!sta.empty()) sta.pop();
- }
- int main() {
- while (1) {
- read(n);
- ++cas;
- if (!n) return 0;
- init();
- int u, v;
- for (int i = 1; i <= n; ++i) {
- read(u), read(v);
- insert(u, v), insert(v, u);
- N = max(max(u, v), N);
- }
- tarjan();
- solve();
- }
- return 0;
- }
- //这个排版好难……