hdu5304 基環 外向樹

來源:互聯網
上載者:User

標籤:

題意:

一個無向圖,n個點,m條邊,要求刪掉m-n條邊使得圖任然連通,問方案數。

 

思路:

留下n條邊,相當於一個樹+一條邊,這樣就有一個唯一的環。

枚舉這個環並看成一個點,就相當於做產生樹計數

1、找環

  s是一個二進位狀態,u為s中編號最小的點

  dp[s][v] 以u為起點,經過s中所有的點,並以u結尾的路徑有多少條

  這樣的環如果u,v有直接的邊相連,再加上dp[s][v]裡的路徑,就可以得到若干個環

  每個環順時針被算一次,逆時針被算一次,所以要除以2

2、產生樹計數

  構造基爾霍夫矩陣,環中的點看成一個點

  這樣可能會有重邊,必須要保留,基爾霍夫矩陣可以計算重邊的情況

  高斯消元的時候不是實數 要模數

 

#include <cstdio>#include <cstring>#include <vector>using namespace std;typedef long long ll;const int N = 17;const int S = 1<<N;const int mod = 998244353;ll dp[S+2][N+2], num[S+2];int n, m, lim;int g[N+5][N+5], id[N+5];ll f[N+5][N+5];int t[N+5], st[S+2];vector<int> ch[S+2];inline void add(ll& x, ll y){    x += y;    if (x >= mod) x -= mod;    if (x < 0) x += mod;}inline ll gcd(ll p, ll q){    ll r;    while (q){        r = p%q;        p = q;        q = r;    }    return p;}inline ll fpow(ll x, int n){    ll ret = 1;    while (n){        if (n&1) ret = ret*x%mod;        x = x*x%mod;        n >>= 1;    }    return ret;}inline void prepare(){    lim = 1<<N;    for (int i=1; i<=N; i++) t[i] = 1<<i-1;    for (int s=0; s<lim; s++){        ch[s].clear();        for (int i=1; i<=N; i++) if (s&t[i]){            if (ch[s].size() == 0) st[s] = i;            ch[s].push_back(i);        }    }}inline void circle(){    for (int s=0; s<lim; s++)    for (int i=0; i<ch[s].size(); i++){        int u = ch[s][i];        if (!dp[s][u]) continue;        for (int v=1; v<=n; v++) if (v>st[s] && (!(s&t[v])) && g[u][v]){            dp[s|t[v]][v] += dp[s][u];        }    }    for (int s=0; s<lim; s++) if(ch[s].size()>=3){        int u = st[s];        for (int i=0; i<ch[s].size(); i++){            int v = ch[s][i];            if (v==u || !dp[s][v] || !g[u][v]) continue;            num[s] += dp[s][v];        }        num[s] >>= 1;        num[s] %= mod;    }}ll gauss(ll a[][N+5], int n){    for (int i=1; i<=n; i++)    for (int j=1; j<=n; j++)        a[i][j] = a[i][j]%mod;    ll ret = 1;    for (int i=2; i<=n; i++){        for (int j=i+1; j<=n ;j++) while (a[j][i]){ // 類似與輾轉相除的消元            ll t = a[i][i]/a[j][i];            for (int k=i; k<=n; k++)                add(a[i][k], -t*a[j][k]%mod);            for (int k=i; k<=n; k++)                swap(a[i][k], a[j][k]);            ret = -ret;        }        if (!a[i][i]) return 0;        ret = ret*a[i][i]%mod;    }    return (ret+mod)%mod;//    for (int i=1; i<=n; i++)//        for (int j=1; j<=n; j++){//            if (a[i][j]<0) a[i][j] += mod;//        }//    ll ret = 1, val = 1;//    for (int i=1; i<n; i++){//        if (a[i][i]<0){//            ret = -ret;//            for (int j=i; j<=n; j++) a[i][j] = -a[i][j];//        }//        for (int j=i+1; j<n; j++) if (a[j][i]){//            if (a[j][i]<0){//                ret = -ret;//                for (int k=i; k<=n; k++) a[j][k] = -a[j][k];//            }//            ll div = gcd(a[i][i], a[j][i]);//            ll tmp = a[i][i]/div;//            ll num = a[j][i]/div;//            val = val*tmp%mod;//            for (int k=i; k<=n; k++)//                a[j][k] = (a[j][k]*tmp-a[i][k]*num+mod)%mod;//        }//        if (a[i][i] == 0) return 0;//        ret = ret*a[i][i]%mod;//    }//    if (val!=1) ret = ret*fpow(val, mod-2)%mod;//    return (ret+mod)%mod;}inline void solve(){    ll ans = 0;    for (int s=0; s<lim; s++) if (num[s]){        memset(id, 0, sizeof(id));        int sign = 1;        for (int i=1; i<=n; i++){            if (s&t[i]) id[i] = 1;  // s中的點縮成編號為1的新點            else id[i] = ++sign;        }        memset(f, 0, sizeof(f));        for (int i=1; i<=n; i++)        for (int j=i+1; j<=n; j++) if(g[i][j] && id[i]!=id[j]){            f[id[i]][id[i]] ++; // 對角上度數+1            f[id[j]][id[j]] ++;            f[id[i]][id[j]] --; // id[i]與id[j]之間多一條邊            f[id[j]][id[i]] --;        }        ll tmp = gauss(f, sign);        add(ans, num[s]*tmp%mod);            // num[s]表示s可以構造出環的個數            // tmp是s縮成點後的產生樹數目}    printf("%I64d\n", ans);}int main(){freopen("1005.in", "r", stdin);freopen("1005.out", "w", stdout);    prepare();    while (scanf("%d %d", &n, &m) != EOF){        lim = 1<<n;        memset(dp, 0, sizeof(dp));        memset(g, 0, sizeof(g));        int a, b, c;        for (int i=1; i<=m; i++){            scanf("%d %d", &a, &b);            g[a][b] = g[b][a] = 1;            c = max(a, b);            dp[t[a]|t[b]][c] = 1;        }        memset(num, 0, sizeof(num));        circle();        solve();    }    return 0;}

  

 

 

hdu5304 基環 外向樹

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.