此题需要大力讨论,WA了三次
首先,肯定要跑一遍缩点,缩完是一棵树
发现只连了一个割点的dcc一定要有一个出口,不然割点断了就GG了,即使这个出口断了,也能找到“另一个”出口
注意,这里推到的前提条件,“另一个出口”,如果这个出口不存在怎么办?即缩完就剩一个点了,这种情况待会再说,不妨先讨论至少有俩点
回来,我们显然可以在每个只“连了一个割点的dcc”放一个出口,方案数用乘法原理一乘即可
再次注意,这里怎么求“只连了一个割点的DCC”
一种显然的思路是缩完点在遍历一次/缩点时存下每个强联通分量都有哪些点,这显然不方便
我一开始考虑边缩点边求,最终又犯了一个错误,即“根节点”在没跑完缩点之前,我们不知道它是不是个割点,所以需要特判,如果它是割点,则累加正确,不是割点的话会在它所属的dcc(唯一),多累加一个割点数目,我们应该减去。
然后考虑就缩成一个点怎么办,那必须要放俩出口,来防止单一出口爆炸的情况,注意这里的前提条件,“能放下两个出口么?即n=1时怎么办”,发现题目里没有直接给出n,而是通过边间接给出,那么n至少为2,不会有坑
方案数显然为n选2的组合数
到此已经可以AC,但仍没有结束
题面并没有保证图是连通的,如果直接划分成多个dcc,很容易在特判“只有一个强联通分量”的情况出错,比较好的实现方法是在做之前直接划分出联通块,划归成子问题,最后乘法原理累加答案
题面并没有保证点的编号的范围,也就是说需要离散化
upd: 代码已更新,考虑了多个联通块的问题,去掉了离散化
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define rint register int
#define lint long long
#define isnum(x) ('0' <= (x) && (x) <= '9')
template<typename tint>
inline void readint(tint& x) {
int f = 1; char ch = getchar(); x = 0;
for(; !isnum(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isnum(ch); ch = getchar()) x = x * 10 + ch - '0';
x *= f;
}
using namespace std;
const int maxn = 100000 + 10;
const int maxm = 2 * maxn;
int n, m;
int head[maxn], ev[maxm], nxt[maxm], totedge = 1;
inline void addedge(int nu, int nv) {
ev[++totedge] = nv, nxt[totedge] = head[nu], head[nu] = totedge;
}
int dfn[maxn], low[maxn], totdfn = 0;
int s[maxn], stop = 0;
int cut[maxn], totdcc = 0;
vector<int> dcc[maxn];
// 点双联通分量不要在线!!
void dfs(int x, int fa) {
dfn[x] = low[x] = ++totdfn, s[++stop] = x;
if(head[x] == 0) ++totdcc, dcc[totdcc].push_back(x); // 特判孤立点
for(rint i=head[x], y=ev[i]; i; i=nxt[i], y=ev[i]) {
if(dfn[y] == 0) dfs(y, x), low[x] = min(low[x], low[y]);
else low[x] = min(low[x], dfn[y]);
}
if(fa && low[x] >= dfn[fa]) { // 根节点不需要
++totdcc, cut[fa]++;
do dcc[totdcc].push_back(s[stop]);
while(s[stop--] != x);
dcc[totdcc].push_back(fa);
}
if(fa == 0) cut[x] = cut[x] >= 2 ? cut[x] : 0;
}
void clear() {
int msize = sizeof(int) * (n + 1);
memset(head, 0, msize), totedge = 1;
memset(dfn, 0, msize), memset(low, 0, msize), totdfn = 0;
memset(cut, 0, msize);
for(rint i=1; i<=totdcc; i++) dcc[i].clear();
stop = totdcc = 0;
n = 0;
}
int main() {
int T = 0;
while(cin >> m && m) {
int nu, nv;
while(m--) {
readint(nu), readint(nv);
n = max(n, max(nu, nv));
addedge(nu, nv), addedge(nv, nu);
}
int ans_cnt = 0;
lint ans_det = 1;
for(rint x=1; x<=n; x++) {
if(dfn[x]) continue;
int pre = totdcc + 1;
dfs(x, 0);
if(pre == totdcc) {
if(dcc[totdcc].size() > 1)
ans_cnt += 2, ans_det *= dcc[totdcc].size() * (dcc[totdcc].size() - 1) / 2;
} else {
for(rint i=pre; i<=totdcc; i++) {
int now_cut = 0;
for(rint j=0; j<dcc[i].size(); j++) {
if(cut[dcc[i][j]]) now_cut++;
}
if(now_cut == 1) ans_cnt++, ans_det *= dcc[i].size() - 1;
}
}
}
printf("Case %d: %d %lld\n", ++T, ans_cnt, ans_det);
clear();
}
return 0;
}
大佬,你的这个代码现在WA了
强力大佬的空间就是 题解数寥寥无几。 leetcode为空。 每道题解都是高难题目.
(其实是我之前懒得发题解,题解都以打卡的形式发了
%%%夏日大佬!