目录
- 题目链接
- 图论的相关定义
- 题解
- 代码
好不错的一道题,花了大量知识ac了
题目链接
https://www.luogu.com.cn/problem/P3225
图论的相关定义
https://www.cnblogs.com/poi-bolg-poi/p/12183166.html
可直接在这看↓
题解
起初做的时候以为,寻找每个割点,在割点形成的联通块中判断。
每有一个联通块,ans1 ++; 联通块size相乘 得到ans2。
后来一个点都没过,思路是错误的。
正解 :
上边方法的错误在ans1, ans1错了 所以ans2也错了。
首先我们可以知道,求割点是必要的。
对于割点形成的点双,有以下几种情况
之所以分为这三种情况, 是因为所产生的答案计数不同。
开始分类讨论
$ 1° $ 不存在割点。
当出口矿点坍塌的时候,必然有需要另一个矿点可以作为出口.
每个点双彼此之间由割点连接构成,不存在割点即此点双完全不可到其他点双,需要单独考虑。
也就是在当前点双中任选两个点作为出口,$ C(n,2) $种可选情况.
$ 2 ° $ 一个割点。
当割点所在矿点坍塌时,必然会使图不连通
当该割点塌陷的时候,图就不会联通,必然要在当前点双里在建一个出口
点双$ { $ $ 1,6,2,3,5 $ $ } $ $中,除去1点,皆符合情况。
$ 3° $ 多个割点。
因为存在多个割点,任意一个矿点坍塌都不会对图的连通性造成影响
当当前所在点双的某个割点塌陷后,图依然联通,必然可以走到其他的点双,从其他点双的出口出去,对答案无影响。
综上所述(雾) 。。。。
代码
挺重要的,还是要看看怎么写
#include
#define clear(a) memset(a, 0, sizeof a)
using namespace std;
const int N = 5e2+50;
struct node{ int next, to; }edge[N<<1];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node) {head[from], to}, head[from] = cnt;}
int low[N], dfn[N], visited[N], cut[N];
int tot, sky, size, num;
void tarjan(int u, int f) {
low[u] = dfn[u] = ++tot; int child = 0;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(!dfn[v]) {
tarjan(v, f), low[u] = min(low[u], low[v]);
if(u == f) child ++;
if(u != f && low[v] >= dfn[u]) cut[u] = 1;
}else low[u] = min(low[u], dfn[v]);
}
if(u == f && child >= 2) cut[u] = 1;
}
void dfs(int u) {
size++, visited[u] = sky;
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(visited[v] != sky && cut[v]) num ++, visited[v] = sky;
if(!visited[v] && !cut[v]) dfs(v);
}
}
int main() {
int n = 0, m, t = 0;
while(1) {
clear(head), clear(edge), clear(cut);
clear(visited), clear(low), clear(dfn);
t++;
n = cnt = tot = 0;
scanf("%d", &m);
if(m == 0) break;
for(int i = 1; i <= m; i++) {
int a, b; scanf("%d%d", &a, &b);
add(a, b), add(b, a);
n = max(max(a, b), n);
}
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i, i);
long long ans1 = 0, ans2 = 1;
for(int i = 1; i <= n; i++) {
if(!visited[i] && !cut[i]) {
num = size = 0, sky++,dfs(i);
if(num == 0) ans1 += 2, ans2 *= size* (size - 1) / 2;
if(num == 1) ans1 ++, ans2 *= size;
}
}
printf("Case %d: %lld %lld\n", t, ans1, ans2);
}
return 0;
}