牛客練習賽56.小雀和他的王國 (無向圖縮點+樹的直徑)

題目鏈接:https://ac.nowcoder.com/acm/contest/3566/E

題目大意:

給定一個無向圖,初始時無向圖內的任意兩點均有若干條路徑,現在你可以加一條邊,使得下一次隨機刪除一條邊時,刪除的邊讓某兩個城市變成無法連接的概率最小,求最小概率。

思路:

一眼就可以看出,如果刪除的是橋,那麼就會形成兩個城市無法連接的情況。

所以我加邊的時候就需要儘可能地讓一些橋變成不是橋的路徑。

怎麼做呢?很明顯,如果是一條鏈,鏈上的邊都是橋,而我只需要將鏈首尾相連,就可以使得這條鏈上的橋都不再是橋。

而且我們注意到,Tarjan邊雙聯通縮點之後,原圖就變成了一顆樹,樹上的邊都是橋,所以我們只需要在樹上找到最長的鏈,將這條鏈上的邊都變成不是橋的路徑,這樣我們就可以去除最多的橋,也是最優的策略了。

綜上,先縮點建樹,再求一遍樹的直徑,答案就出來了。

#include<bits/stdc++.h>
#include<vector>
#include<stack>
#define int long long
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int mod=1e9+7;
typedef long long LL;
bool in[maxn]; int belong[maxn]; int bct; int Time; int dfn[maxn]; int low[maxn];
stack<int> s;
vector<int> e[maxn],G[maxn];
int n,m,len,id;

void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++Time;
    s.push(u);
    in[u]=true;
    int sz=e[u].size();
    bool tag=false;
    for(int i=0;i<sz;i++)
    {
        int v=e[u][i];
        if(v==fa&&!tag)
        {
            tag=true;
            continue;
        }
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else if(in[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int v;
        ++bct;
        do{
            v=s.top(); s.pop();
            belong[v]=bct;
            in[v]=false;
        }while(v!=u);
    }
}
void tarjan_init()
{
    bct=Time=0;
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
    for(int u=1;u<=n;u++)
    {
        int sz=e[u].size();
        for(int j=0;j<sz;j++)
        {
            int v=e[u][j];
            if(belong[u]==belong[v]) continue;
            G[belong[u]].push_back(belong[v]);
        }
    }
}
void dfs(int u,int fa,int step)
{
    if(step>len) { len=step; id=u; }
    int sz=G[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=G[u][i];
        if(v==fa) continue;
        dfs(v,u,step+1);
    }
}
//擴展GCD
ll ex_gcd(ll a,ll b,ll &x,ll &y){
   if(b==0){x = 1ll;y = 0ll;return a;}
   ll g = ex_gcd(b,a%b,x,y);
   ll temp = x;
   x = y;
   y = temp - a/b*y;
   return g;
}
//擴展歐幾里得的另一種寫法
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){
    //求解ax+by=gcd(a,b)的一組解
    if(!b){
        d=a,x=1ll,y=0ll;
    }
    else{
        ex_gcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}


//逆元
ll inv(ll a,int mod){
   ll X,Y;
   ll g = ex_gcd(a,mod,X,Y);
   if(g!=1)return -1;
   return (X%mod + mod)%mod;
}

signed main(void)
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%lld%lld",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    tarjan_init();
    len=0;
    dfs(1,0,0);
    len=0;
    dfs(id,0,0);
    int ans=inv(m+1,mod)*(bct-1-len)%mod;
    printf("%lld\n",ans);
}

 

發佈了216 篇原創文章 · 獲贊 15 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章