牛客练习赛56 E.小雀和他的王国 tarjan+生成树上求直径

原题链接:https://ac.nowcoder.com/acm/contest/3566/E

题意

有n个点,m条边的图,开始保证图联通,你可以加上一条边,求任意去掉一条边后存在两点不连通的概率。

分析

非常有意思的题目,转化一下题意就是求添上一条边之后
桥 的 个 数 / 边 的 总 数 桥的个数/边的总数 /

然后我们已知在一条树上,添上一条边之后,该链上所有的桥都没了,因此我们只要求树的直径就可以了,这样可以消除最多的桥。

但本题是在图上而不是在树上,其实仔细观察生成树,发现所有桥一定存在于该图任意生成树的边上。因此我们只要随意求一个生成树,然后将原图中的桥标记出来,记为距离1,别的边记为0,这样再用树形DP跑一遍树的直径就可以了。

本题有个坑点,图中是存在重边的,我用unordered_map存了重边然后跑tarjan求桥就可以过了。

Code

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<ll, ll> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10, M = 1e6 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
struct Edge {
    int to, next, w;
}e[M], e2[M];
struct node {
    int u, v;
}ed[M];
int cnt, h[N], dfn[N], low[N], idx;
int h2[N], cnt2, brg_num;
int fa[N], ans, dp[N];
map<int, map<int, int>> brg;
unordered_map<int, map<int, int>> edge;
void add(int u, int v) {
    e[cnt].to = v;
    e[cnt].next = h[u];
    h[u] = cnt++;
}
void add2(int u, int v, int w) {
    e2[cnt2].to = v;
    e2[cnt2].next = h2[u];
    e2[cnt2].w = w;
    h2[u] = cnt2++;
}
void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++idx;
    for (int i = h[u]; ~i; i = e[i].next) {
        int v = e[i].to;
        if(v == fa) continue;
        if (!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u] && edge[u][v] == 1) brg[u][v] = brg[v][u] = 1, brg_num++;
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
}
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void dfs(int x, int far) {
    for (int i = h2[x]; ~i; i = e2[i].next) {
        int v = e2[i].to;
        if (v == far) continue;
        dfs(v, x);
        ans = max(ans, e2[i].w + dp[v] + dp[x]);
        dp[x] = max(dp[x], dp[v] + e2[i].w);
    }
}
ll ksm(ll a, ll b) {
    ll res = 1, base = a;
    while (b) {
        if (b & 1) res = res * base % MOD;
        base = base * base % MOD;
        b >>= 1;
    }
    return res;
}
void solve() {
    int n, m; cin >> n >> m;
    memset(h, -1, sizeof h);
    memset(h2, -1, sizeof h2);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        add(u, v), add(v, u);
        edge[u][v]++, edge[v][u]++;
        ed[i] = {u, v};
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) tarjan(i, 0);
    }
    for (int i = 1; i <= m; i++) {
        int u = ed[i].u;
        int v = ed[i].v;
        int fu = find(u);
        int fv = find(v);
        if (fu != fv) {
            fa[fu] = fv;
            add2(u, v, brg[u][v]);
            add2(v, u, brg[v][u]);
        }
    }
    dfs(1, 0);
    ll left = brg_num - ans;
    cout << left * ksm(m+1, MOD-2) % MOD << endl;
}


signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值