思路:
1.如果删掉的是桥,那么将图分成两个部分,设它们的大小分别为a和b,我们为了保证图的连通性,只能连两个部分之间的边,即a*b-1种可能
2.如果删掉的不是桥,那么就是简单环上的边,删掉后环变成了小树,而且我们只能连小树上的点之间的边,否则就会出现一条边在两个简单环中状况,
就不是仙人掌图了(画画图就知道了)。一颗大小为x的小树的贡献是x*(x-1)/2-(x-1),我们可以先把所有小树的贡献和算出来,然后对于每个简单环单独处理,
对于某个简单环我们减去以环上每个点为根的小树的贡献,然后加上断环后形成的新树的贡献就可以知道对于这个环上每条边删掉后的贡献了。
具体实现就是用点双联通求简单环,把桥用并查集连接起来形成小树。
代码:
#pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include<bits/stdc++.h> using namespace std; #define y1 y11 #define fi first #define se second #define pi acos(-1.0) #define LL long long //#define mp make_pair #define pb emplace_back #define ls rt<<1, l, m #define rs rt<<1|1, m+1, r #define ULL unsigned LL #define pll pair<LL, LL> #define pli pair<LL, int> #define pii pair<int, int> #define piii pair<pii, int> #define pdd pair<double, double> #define mem(a, b) memset(a, b, sizeof(a)) #define debug(x) cerr << #x << " = " << x << "\n"; #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); //head const int N = 1e5 + 5; vector<int> g[N]; vector<pair<int,int>> bcc[N]; int low[N], dfn[N], sz[N], cc[N], tot = 0, top = 0, cnt = 0, n, m; pair<int,int> stk[N]; int fa[N]; int Find(int x) { if(x == fa[x]) return x; else return fa[x] = Find(fa[x]); } void Merge(int x, int y) { x = Find(x); y = Find(y); if(x == y) return ; fa[x] = y; cc[y] += cc[x]; } LL ans, sum; void tarjan(int u, int fa) { low[u] = dfn[u] = ++tot; sz[u] = 1; for (int v : g[u]) { pair<int,int> e = {u, v}; if(!dfn[v]) { stk[++top] = e; tarjan(v, u); sz[u] += sz[v]; low[u] = min(low[u], low[v]); if(low[v] > dfn[u]) ans += sz[v]*1LL*(n-sz[v])-1; if(low[v] >= dfn[u]) { cnt++; while(stk[top] != e) { bcc[cnt].push_back(stk[top--]); } bcc[cnt].push_back(stk[top--]); } } else if(v != fa ) { if(dfn[v] < dfn[u]) stk[++top] = e, low[u] = min(low[u], dfn[v]); } } } int u, v, k; int main() { freopen("cactus.in", "r", stdin); freopen("cactus.out", "w", stdout); scanf("%d %d", &n, &m); while(m--) { scanf("%d %d", &k, &v); k--; while(k--) { scanf("%d", &u); g[u].pb(v); g[v].pb(u); v = u; } } tarjan(1, 1); for (int i = 1; i <= n; ++i) fa[i] = i, cc[i] = 1; for (int i = 1; i <= cnt; ++i) if(bcc[i].size() == 1) Merge(bcc[i][0].fi, bcc[i][0].se); for (int i = 1; i <= n; ++i) if(fa[i] == i) sum += cc[i]*1LL*(cc[i]-1)/2-(cc[i]-1); for (int i = 1; i <= cnt; ++i) { if(bcc[i].size() == 1) continue; LL tmp = sum, tot = 0; for (int j = 0; j < bcc[i].size(); ++j) { int u = bcc[i][j].fi; u = Find(u); tmp -= cc[u]*1LL*(cc[u]-1)/2-(cc[u]-1); tot += cc[u]; } ans += (tmp+tot*1LL*(tot-1)/2-(tot-1)-1)*1LL*bcc[i].size(); } printf("%lld\n", ans); return 0; }