hdu 5304 Eastest Magical Day Seep Group's Summer(基环 外向图)

题意:一幅无向图有n个点m条边,求删去m-n条边图依然联通的方案数;

参考:http://blog.csdn.net/starry__night/article/details/46756587

        http://www.cnblogs.com/AlpcFlagship/p/4675921.html

思路:剩下n条边,则可看做一棵生成树加一条边,有一个唯一的环;为基环外向图;

        通过dp[s][v]保存路径找环,环缩点后图变为生成树;

        通过基尔霍夫矩阵高斯消元生成树计数;

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mo=998244353;
struct atom{
    int x,y;
};
atom operator - (const atom k1,const atom k2){
    return (atom){(k1.x-k2.x+mo)%mo,(k1.y-k2.y+mo)%mo};
}
atom operator * (const atom k1,const int k2){
    return (atom){1ll*k1.x*k2%mo,1ll*k1.y*k2%mo};
}
int x[20][20],n,m,A[20][20],dp[1<<16][17],f[1<<16],pd[20];
int gauss(int n){
    int pd=1; n--;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++) x[i][j]=(x[i][j]+mo)%mo;
    for (int i=1;i<=n;i++){
        int r=0;
        for (int j=i;j<=n;j++) if (x[j][i]){r=j; break;}
        for (int j=1;j<=n;j++) swap(x[i][j],x[r][j]);
        if (r!=i) pd=-pd;
        for (int j=i+1;j<=n;j++){
            int k1=x[i][i],k2=x[j][i];
            atom xx=(atom){1,0},y=(atom){0,1};
            while (k2){
                int k4=k1/k2;
                atom z=y; y=xx-y*k4; xx=z;
                k4=k2; k2=k1%k2; k1=k4; pd=-pd;
            }
            for (int k=1;k<=n;k++){
                int k3=x[i][k],k4=x[j][k];
                x[i][k]=(1ll*xx.x*k3+1ll*xx.y*k4)%mo;
                x[j][k]=(1ll*y.x*k3+1ll*y.y*k4)%mo;
            }
        }
    }
    for (int i=1;i<=n;i++) pd=1ll*pd*x[i][i]%mo;
    return (pd+mo)%mo;
}
int solve(){
    memset(A,0x00,sizeof A);
    for (;m;m--){
        int k1,k2; scanf("%d%d",&k1,&k2); A[k1][k2]=1; A[k2][k1]=1;
    }
    memset(f,0x00,sizeof f);
    for (int s=1;s<=n;s++){
        memset(dp,0x00,sizeof dp);
        dp[(1<<s-1)][s]=1;
        for (int i=1;i<(1<<n);i++)
            for (int j=1;j<=n;j++)
                if (dp[i][j]){
                    for (int k=s+1;k<=n;k++)
                        if ((i&(1<<k-1))==0&&A[j][k])
                            dp[i|(1<<k-1)][k]=(dp[i|(1<<k-1)][k]+dp[i][j])%mo;
                    if (A[j][s]) f[i]=(f[i]+dp[i][j])%mo;
                }
    }
    int ans=0;
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            if (A[i][j]) f[(1<<i-1)|(1<<j-1)]=(f[(1<<i-1)|(1<<j-1)]-1+mo)%mo;
    int inv2=(mo+1)/2;
    for (int i=0;i<(1<<n);i++) f[i]=1ll*f[i]*inv2%mo;
    for (int now=0;now<(1<<n);now++) if (f[now]){
        for (int i=1;i<=n;i++) pd[i]=0;
        for (int i=1;i<=n;i++) if (now&(1<<i-1)) pd[i]=1;
        int sign=1;
        for (int i=1;i<=n;i++) if (pd[i]==0) pd[i]=++sign;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++) x[i][j]=0;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (A[i][j]&&pd[i]!=pd[j]){
                    x[pd[i]][pd[j]]--; x[pd[i]][pd[i]]++;
                }
        ans=(ans+1ll*f[now]*gauss(sign))%mo;
    }
    return ans;
}
int main(){
//    freopen("data.in","r",stdin);
//    freopen("data.out","w",stdout);
    while (scanf("%d%d",&n,&m)!=EOF) printf("%d\n",solve());
    return 0;
}

 

你可能感兴趣的:(group)