1. 程式人生 > >【歐拉回路+最小生成樹】SD開車@山東2018省隊一輪集訓day1

【歐拉回路+最小生成樹】SD開車@山東2018省隊一輪集訓day1

目錄

【歐拉回路+最小生成樹】SD開車@山東2018省隊一輪集訓day1

PROBLEM

題目描述

作為欽欽草原最綠的男人,楊某針每天都要開車巡視欽欽草原一圈。
欽欽草原由 n 個城市組成,m 條雙向道路連線著它們。經過第 i 條道路要花費的時間是\(2^i\)
楊某針想要經過每條道路至少一次,在此基礎上他想最小化他花費的時間。但作為 曾經 CTSC 的 Cu 選手,他並不能很快地計算出這個問題。所以他向你求助。

輸入

輸入第一行包含兩個正整數n,m。
接下來m行,每行兩個正整數\(a_i\),\(b_i\),表示第i條邊連線點\(a_i\)\(b_i\),它的權值為\(2^i\)。保證\(a_i\neq b_i\),不存在重邊,且任意兩個點之間可以互相到達。

輸出

輸出一行一個整數,表示答案對\(10^9\)+7取模的值。

樣例輸入

4 5
1 2
3 4
2 3
1 3
2 4

樣例輸出

70

提示

最優的路線應當為 1-2-3-4-2-3-1。
對於20%的資料,n,m≤20。
對於40%的資料,n,m≤2,000。
對於100%的資料,n≤400,000,m≤500,000。

SOLUTION

若存在一條從節點S出發的路徑,恰好不重不漏地經過每條邊一次(可以重複經過圖中的節點),最終回到起點S,則稱該路徑為歐拉回路

。存在歐拉回路的無向圖被稱為尤拉圖

要經過每條道路至少一次,可以對比在歐拉回路中,每條邊恰好經過一次。若最佳路線經過某一條邊n+1次,可以看作在原圖中加上了n次那條邊。由原圖G加上重複走的邊得到G',G'一定是一個尤拉圖,由尤拉圖的性質,G'中每個點的度一定為偶數。
題目要求最小花費,也就是要加的邊的權值和最小。若我分若干次去新增邊,這裡可以貪心假設,每次加的一個邊集一定是某兩個度為奇數的點的最短路徑上的邊,然後使得這兩個點的度變為偶數。
而至於如何去求兩個奇數度點的最短路,可以從邊的權值上下手——第i條邊的權值是\(2^i\)。用求最小生成樹的Kruskal演算法,我們從按編號(也就是權值)小到大列舉邊,然後並查集維護,建出一棵最小生成樹。因為\(2^n = 2^{n-1} + 2^{n-2} + ... + 2^1+ 2\)

,所以生成樹上兩點距離就是原圖中兩點最短距離(這裡需要仔細思考)。
最後dfs遍歷生成樹,回溯時判斷當前點的度數是否為奇數,如果是奇數,讓答案再加上該點和它的父節點所連的邊的權值,並更新兩點的度數。
最終答案要加上原圖中所有邊的權值和。

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 4e5 + 5;
const int MAXE = 5e5+5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9+7;
 
int degree[MAXN];
int u[MAXE],v[MAXE];
 
struct edge{
    int u,v,w,nex;
}ed[MAXN<<1];
 
int head[MAXN],tot;
 
void addedge(int uu,int vv,int w){
    tot++;
    ed[tot].u = uu;
    ed[tot].v = vv;
    ed[tot].w = w;
    ed[tot].nex = head[uu];
    head[uu] = tot;
}
 
int fa[MAXN];
 
int DjsGet(int x){
    if(x==fa[x])return x;
    return fa[x] = DjsGet(fa[x]);
}
 
ll fastpow(ll a,ll n){
    ll res = 1;
    while(n){
        if(n&1)res=res*a%MOD;
        a = a*a%MOD;
        n>>=1;
    }
    return res;
}
 
ll ans;
 
void dfs(int u,int p){
    for(int i=head[u];i;i = ed[i].nex){
        int v = ed[i].v;
        if(i!=(p^1))
            dfs(v,i);
    }
    if(degree[u]&1){
        ans=(ans+fastpow(2,ed[p].w))%MOD;
        degree[u]++;
        degree[ed[p].u]++;
    }
}
 
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u[i],&v[i]);
        degree[u[i]]++;
        degree[v[i]]++;
    }
    tot++;
    for(int i = 1;i<=n;i++)fa[i] = i;
    for(int i=1;i<=m;i++){
        int fx = DjsGet(u[i]),fy = DjsGet(v[i]);
        if(fx!=fy){
            fa[fx] = fy;
            addedge(u[i],v[i],i);
            addedge(v[i],u[i],i);
        }
        ans=(ans+fastpow(2,i))%MOD;
    }
    dfs(ed[2].u,0);
    printf("%lld\n",ans);
    return 0;
}