UVALive 5135 Mining Your Own Bussiness【tarjan点双】

时间:2024-04-04 12:38:09

LINK1

LINK2


题目大意

给你一个无向连通图,让你给一些点染上黑色,需要满足染色之后,断开任意一个节点,要满足任意一个联通块中剩下的节点中至少有一个黑点

思路

一开始想的是把每一个点双联通分量都把除了割点的size乘上

然后发现随手卡掉

然后发现一下性质

首先所有相邻点双联通分量一定有公共的割点

如果一个双联通分量里面只包含了一个割点,那么如果断掉这个割点那么这个双联通分量就被孤立了

所以这样的双联通分量至少选择一个点

然后如果一个双联通分量有大于等于两个割点,就算一个被割掉了另外一边至少连接着1个只有一个割点的点双联通分量

那么就很容易做了

特判一下如果整张图都是一个双联通分量那么就是任意选两个点就可以了


//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
//typename
typedef long long ll;
//convenient for
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
//inf of different typename
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
//fast read and write
template <typename T>
void Read(T &x) {
bool w = 1;x = 0;
char c = getchar();
while (!isdigit(c) && c != '-') c = getchar();
if (c == '-') w = 0, c = getchar();
while (isdigit(c)) {
x = (x<<1) + (x<<3) + c -'0';
c = getchar();
}
if (!w) x = -x;
}
template <typename T>
void Write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) Write(x / 10);
putchar(x % 10 + '0');
}
//----------------------------------------------
typedef pair<int, int> pi;
#define fi first
#define se second
const int N = 5e4 + 10;
struct Edge {
int v, nxt;
} E[N << 1];
stack<pi > st;
int head[N], tot = 0, ind = 0, cnt_bcc = 0;
int n, m, dfn[N], low[N], siz[N], bel[N];
bool iscut[N];
vector<int> g[N];
void init() {
fu(i, 1, N - 1) dfn[i] = low[i] = bel[i] = head[i] = siz[i] = 0, iscut[i] = 0;
tot = ind = cnt_bcc = 0;
}
void add(int u, int v) {
E[++tot] = (Edge) {v, head[u]};
head[u] = tot;
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++ind;
int num = 0;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa) continue;
if (!dfn[v]) {
st.push(pi(u, v));
++num;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
iscut[u] = 1;
g[++cnt_bcc].clear();
pi now;
do {
now = st.top(); st.pop();
if (bel[now.fi] != cnt_bcc) {
g[cnt_bcc].push_back(now.fi);
bel[now.fi] = cnt_bcc;
}
if (bel[now.se] != cnt_bcc) {
g[cnt_bcc].push_back(now.se);
bel[now.se] = cnt_bcc;
}
} while (now.fi != u || now.se != v);
}
} else low[u] = min(low[u], dfn[v]);
}
if (num < 2 && !fa) iscut[u] = 0;
}
void solve() {
n = 0;
init();
fu(i, 1, m) {
int u, v;
Read(u), Read(v);
add(u, v);
add(v, u);
n = max(n, max(u, v));
}
tarjan(1, 0);
if (cnt_bcc == 1) {
Write(2), putchar(' ');
Write(ll(n) * ll(n - 1) / 2), putchar('\n');
} else {
ll ans1 = 0, ans2 = 1;
fu(i, 1, cnt_bcc) {
int cntnow = 0;
fv(j, g[i])
if (iscut[g[i][j]]) ++cntnow;
if (cntnow == 1)
++ans1, ans2 *= (ll) g[i].size() - 1;
}
Write(ans1), putchar(' ');
Write(ans2), putchar('\n');
}
}
int main() {
#ifdef dream_maker
freopen("input.txt", "r", stdin);
#endif
int id = 0;
while (1) {
Read(m);
if (!m) return 0;
printf("Case %d: ", ++id);
solve();
}
return 0;
}