Codeforces Round #686 (Div. 3) E. Number of Simple Paths

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(n1)szi(szi1) 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);
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值