题意
题解
已知一个无向连通图,求删除两条边后使图不连通的方案数。
考虑 D F S DFS DFS 树。若删除的边中至少有一条割边,则一定满足条件。之后仅考虑不包含割边的情况。若删除的两条边都是非树边,则无法使图不连通;若删除的边中仅有一条非树边,那么仅当这条非树边被唯一的非树边覆盖时,才能使图不连通;若删除的两条边都是树边,则应该满足两条边被同一组非树边覆盖,条件等价于这两条树边 ( u , v ) , ( x , y ) (u,v),(x,y) (u,v),(x,y) 被非树边覆盖的次数相同,且位置靠近叶子的边,假设为 ( u , v ) (u,v) (u,v),已其深度较大的节点 u u u 为根的子树中的返祖边所能到达的深度小于 u u u 的节点中,深度最大的节点的深度小于 x , y x,y x,y 中深度较小的节点(令其为 f ( u ) f(u) f(u))。
每一条边可以在其深度较大的节点处进行维护。割边可以在求 DFS 树时求出,树边被非树边覆盖的次数可以进行树上差分求解。 f ( u ) f(u) f(u) 可以在 DFS 中递归求解,具体而言,维护每一个节点连接的非树边中的返祖边(指向深度小于其的节点)所连接的节点的集合,在回溯至节点 u u u 时,通过时间戳 O ( n ) O(n) O(n) 遍历子树,将子孙节点所维护的集合中深度大于等于 u u u 的节点弹出,同时更新 f ( u ) f(u) f(u)。
总时间复杂度 O ( n 2 + m ) O(n^2+m) O(n2+m)。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
const int MAXN = 2E3 + 5;
struct edge
{
int to, rev;
bool tree;
};
int N, M, cut;
vector<edge> G[MAXN];
int dfn[MAXN], low[MAXN], f[MAXN], sum[MAXN];
int idx[MAXN], L[MAXN], R[MAXN];
vector<int> rec[MAXN];
ll res;
void _min(int &x, int y) { x = min(x, y); }
void _max(int &x, int y) { x = max(x, y); }
void add_edge(int u, int v)
{
G[u].pb({v, (int)G[v].size(), 0});
G[v].pb({u, (int)G[u].size() - 1, 0});
}
void dfs(int u, int &k)
{
idx[k] = u, L[u] = k;
dfn[u] = low[u] = k++;
rec[u].clear();
for (auto &e : G[u])
{
int v = e.to;
if (dfn[v] == -1)
{
e.tree = G[v][e.rev].tree = 1;
dfs(v, k);
sum[u] += sum[v];
_min(low[u], low[v]);
if (low[v] > dfn[u])
++cut;
}
else if (!e.tree)
{
if (dfn[u] < dfn[v])
continue;
++sum[u], --sum[v];
_min(low[u], dfn[v]);
rec[u].pb(dfn[v]);
}
}
if (sum[u] == 1)
++res;
R[u] = k;
f[u] = -1;
sort(rec[u].begin(), rec[u].end());
for (int i = L[u]; i < R[u]; ++i)
{
int v = idx[i];
while (rec[v].size() && rec[v].back() >= dfn[u])
rec[v].pop_back();
if (rec[v].size())
_max(f[u], rec[v].back());
}
if (sum[u] == 0)
return;
for (int i = L[u] + 1; i < R[u]; ++i)
{
int v = idx[i];
if (sum[v] == sum[u] && f[v] != -1 && f[v] < dfn[u])
++res;
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
while (cin >> N >> M)
{
for (int i = 0; i < N; ++i)
G[i].clear();
res = cut = 0;
for (int i = 0; i < M; ++i)
{
int u, v;
cin >> u >> v;
--u, --v;
add_edge(u, v);
}
for (int i = 0; i < N; ++i)
dfn[i] = -1, sum[i] = 0;
int k = 0;
dfs(0, k);
ll a = M, b = M - cut;
res += a * (a - 1) / 2 - b * (b - 1) / 2;
cout << res << '\n';
}
return 0;
}