Files
python/TangDou/AcWing/DCC/HDU3394.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

102 lines
3.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 2e6 + 10;
//链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int dfn[N], low[N], stk[N], top, timestamp;
vector<int> bcc[N];
int bcnt;
unordered_set<int> in;
int n, m;
int ans1, ans2, ans3;
void deal_circle() {
int sum = 0;
for (int u : bcc[bcnt])
for (int j = h[u]; ~j; j = ne[j])
if (in.count(e[j])) sum++; //此边的两个点都在点双中,则此边在点双中
sum /= 2; //因为a->b,b->a都sum++,重复了,需要除以2,才是边数
//边数大于点数,嵌套环,每条边都是冲突边
if (sum > bcc[bcnt].size()) ans2 += sum;
//判定割边 (第二种判定割边的办法)
//此处写ans3+=sum 或 ans3+=1是一个意思,因为此时只能是两点一线的情况,否则就不是点双。特殊的点双只有一条割边
if (sum < bcc[bcnt].size()) ans3 += sum;
}
void tarjan(int u, int fa) {
low[u] = dfn[u] = ++timestamp;
stk[++top] = u;
int son = 0; //子节点个数
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
if (!dfn[j]) {
son++; //找到一个子节点
tarjan(j, u);
low[u] = min(low[u], low[j]);
//第一种判定割边的办法
//参阅一下判割边的模板代码
if (low[j] > dfn[u]) ans1++;
if (low[j] >= dfn[u]) {
int x;
bcnt++;
//针对每个点双而言,都在dfs过程中记录此点双中有哪些点,避免后期二次统计,造成速度下降TLE
in.clear();
do {
x = stk[top--];
bcc[bcnt].push_back(x);
in.insert(x); //增加点双中有哪些点的记录信息
} while (x != j);
bcc[bcnt].push_back(u);
in.insert(u); //增加点双中有哪些点的记录信息
deal_circle(); //在完成点双的记录后,马上开始统计,以免将所有的点双中点的信息完整记录下来,造成MLE,TLE等问题
}
} else
low[u] = min(low[u], dfn[j]);
}
//特判独立点
if (fa == -1 && son == 0) bcc[++bcnt].push_back(u);
}
int main() {
//文件输入输出
#ifndef ONLINE_JUDGE
freopen("HDU3394.in", "r", stdin);
#endif
while (~scanf("%d%d", &n, &m) && n + m) {
// ans1:割边数量 ans2:冲突边数量,也就是点双中所有边的数量,它们都是存在多个环中 ans3:也是割边的数量,与 ans1相同,但实现的方式不一样
ans1 = ans2 = ans3 = idx = top = timestamp = 0;
memset(dfn, 0, sizeof dfn);
memset(h, -1, sizeof h);
// memset(low, 0, sizeof low); //low数组不用重新初始化
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
a++, b++; //避开下标0,向右推一个,使得下面的 tarjan算法的模板不变
if (a != b) add(a, b), add(b, a); //防止重边
}
// tarjan求点双的模板
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i, -1);
//下面无论是取 ans1还是ans3都是一样的
printf("%d %d\n", ans1, ans2);
// printf("%d %d\n", ans3, ans2);
}
return 0;
}