牛客练习赛56 E.小雀和他的王国 tarjan+生成树上求直径
Posted kaka0010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛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();
}
以上是关于牛客练习赛56 E.小雀和他的王国 tarjan+生成树上求直径的主要内容,如果未能解决你的问题,请参考以下文章
牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第三场)A.不凡的夫夫(斯特林公式) D.小牛vs小客 E.进击吧!阶乘(大数Java) G.大水题(数学)