Problem - E. Number of Simple Paths - Codeforces
题意
给一个 n n n 个点 n n n 条边的联通图,问有多少条简单路径。
解法
显然这是一个基环树,可以看作一个环连了很多棵小树。小树内部任取两点路径数为 1 1 1 ,但是如果经过那个环,就会有两条简单路径。所以总的简单路径数为 n ⋅ ( n − 1 ) − ∑ s z i ⋅ ( s z i − 1 ) n\cdot(n-1)-\sum sz_i\cdot(sz_i-1) n⋅(n−1)−∑szi⋅(szi−1) , s z i sz_i szi 表示每棵小树的大小。
用拓扑排序把除了环的点都删掉,同时用并查集来维护子树的大小,然后统计答案即可。
解法
#pragma region
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 2e5 + 5;
int n;
vector<int> g[maxn];
int d[maxn];
int fa[maxn], sz[maxn];
int findroot(int x) {
return fa[x] == x ? x : fa[x] = findroot(fa[x]);
}
void link(int x, int y) {
int xx = findroot(x), yy = findroot(y);
if (xx != yy) fa[xx] = yy, sz[yy] += sz[xx];
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
rep(i, 1, n) g[i].clear(), d[i] = 0, fa[i] = i, sz[i] = 1;
rep(i, 1, n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
d[u]++, d[v]++;
}
queue<int> q;
rep(i, 1, n) if (d[i] == 1) q.push(i);
while (q.size()) {
int now = q.front();
q.pop();
for (auto nex : g[now]) {
d[nex]--;
link(nex, now);
if (d[nex] == 1) q.push(nex);
}
}
ll ans = 1LL * n * (n - 1);
rep(i, 1, n) if (findroot(i) == i) ans -= 1LL * sz[i] * (sz[i] - 1) / 2;
printf("%lld\n", ans);
}
}