You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
3.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}