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

132 lines
3.2 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;
typedef long long LL;
const int N = 3010, M = N << 4;
const LL mod = 19491001;
// 链式前向星
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 n, m;
// 最简并查集
int p[N];
int find(int x) {
if (x == p[x]) return x;
return p[x] = find(p[x]);
}
LL f[N];
int dfn[N], low[N], ts;
int stk[N], top;
int bcnt;
/*
// 边双模板
vector<int> bcc[N];
int dcc[N];
void tarjan(int u, int from) {
dfn[u] = low[u] = ++ts;
stk[++top] = u;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
} else if (i != (from ^ 1))
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
++bcnt;
int x;
do {
x = stk[top--];
bcc[bcnt].push_back(x); // 记录边双中有哪些点
dcc[x] = bcnt; // 反向记录x这个点在bcnt这个边双里面
} while (x != u);
}
}
*/
int dcc[N]; // 记录每一个节点在哪个边双中
bool vis[M << 1];
void tarjan(int u, int fa) {
low[u] = dfn[u] = ++ts;
stk[++top] = u;
for (int i = h[u]; ~i; i = ne[i]) {
if (vis[i]) continue;
if ((i == fa) || ((i ^ 1) == fa)) continue;
int v = e[i];
if (!dfn[v]) {
vis[i] = vis[i ^ 1] = 1;
tarjan(v, fa);
vis[i] = vis[i ^ 1] = 0;
low[u] = min(low[u], low[v]);
} else
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
dcc[u] = ++bcnt;
while (stk[top] != u)
dcc[stk[top--]] = bcnt;
top--;
}
}
int calc(int u, int v) {
int x = find(u), y = find(v);
if (x != y) return 0;
if (dcc[u] != dcc[v]) return 1;
if (f[u] == f[v]) return 3;
return 2;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("P4214.in", "r", stdin);
#endif
memset(h, -1, sizeof h);
scanf("%d %d", &n, &m); // 节点数和管道数
for (int i = 1; i <= n; i++) p[i] = i; // 初始化并查集
for (int i = 1; i <= m; i++) { // 枚举每条边
int a, b;
scanf("%d %d", &a, &b);
add(a, b), add(b, a);
// 并查集这是在干什么?记录点与点的连通性?
int x = find(a), y = find(b);
if (x != y) p[x] = y;
}
// base case初始化
for (int i = 1; i <= n; i++) f[i] = 1ll;
// 为什么是m+1次呢?
for (int x = 1; x <= m + 1; x++) {
// tarjan初始化
memset(low, 0, sizeof low);
memset(dfn, 0, sizeof dfn);
ts = 0, bcnt = 0, top = 0;
// 跑Tarjan,可能不连通
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i, x << 1);
for (int i = 1; i <= n; i++)
f[i] = f[i] * mod + (LL)dcc[i];
}
// 根据题意,计算最终的结果
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
ans += calc(i, j);
// 输出
printf("%d\n", ans);
return 0;
}