生成树计数

HDU 5304:

题目:给一个n个点,m条边的图,求去掉m-n条边使图连通的方案数.

思路:先状压dp出以某个点集能组成的环的数量,然后缩点用matrixTree定理做.

/*
* @author:  Cwind
*/
#include <bits/stdc++.h>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-4)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;


const ll MOD= 998244353;
const int MAXN = 17;
void exgcd(int a, int b, int &x, int &y) { 
    if (b) { 
        exgcd(b, a % b, y, x); 
        y -= a / b * x ; 
    }  
    else { 
        x = 1; 
        y = 0; 
    } 
} 
int inv(int a){ 
    int x, y, b = MOD; 
    exgcd(a, b, x, y); 
    if (x < 0) x += MOD; 
    return x; 
} 
int n,m;
struct Matrix {
    ll a[20][20];
    Matrix() {
        memset(a, 0, sizeof(a));
    }
    void clear() {
        memset(a, 0, sizeof(a)); 
    }
    ll det(int n)//求行列式的值模上MOD,需要使用逆元
    {
        ll res = 1;
        for(int i = 0;i < n;i++)
        {
            for(int j = i;j < n;j++)
                if(a[j][i]!=0)
                {
                    for(int k = i;k < n;k++)
                        swap(a[i][k],a[j][k]);
                    if(i != j)
                        res = (-res+MOD)%MOD;
                    break;
                }
            if(a[i][i] == 0)
            {
                res = 0;//不存在(也就是行列式值为0)
                break;
            }
            for(int j = i+1;j < n;j++)
            {
                //int mut = (a[j][i]*INV[a[i][i]])%MOD;//打表逆元 
                ll mut = (a[j][i]*inv((int)a[i][i]))%MOD;
                for(int k = i;k < n;k++) {
                    a[j][k] = (a[j][k]-(a[i][k]*mut))%MOD;
                    if(a[j][k] < 0 ) a[j][k] += MOD;
                }
            }
            res = (res * a[i][i])%MOD;
        }
        return res;
    }
}; 

const int maxn=17;
bool G[17][17];
ll dp[1<<17][17];
ll sum[1<<17];
bool inq[1<<17];
int Q[1<<17];
int front,back;
void caldp(){
    memset(inq,0,sizeof inq);
    front=back=0;
    for(int i=0;i<n;i++){
        dp[(1<<i)][i]=1;
        Q[back++]=(1<<i);
        inq[(1<<i)]=1;
    }
    while(front<back){
        int mask=Q[front++];
        int s=__builtin_ffs(mask);s--;
        for(int t=s;t<n;t++){
            if(!(mask&(1<<t))||dp[mask][t]==0) continue;
            if(G[s][t]) sum[mask]=(sum[mask]+dp[mask][t])%MOD;
            for(int j=s+1;j<n;j++){
                if(mask&(1<<j)) continue;
                if(!G[t][j]) continue;
                dp[mask|(1<<j)][j]=(dp[mask|(1<<j)][j]+dp[mask][t])%MOD;
                if(!inq[mask|(1<<j)]&&dp[mask|(1<<j)][j]) 
                    Q[back++]=mask|(1<<j),inq[mask|(1<<j)]=1;
            }
        }
    }
}
int in[17];
Matrix C;
ll solve(){
    ll ans=0;
    for(int i=1;i<(1<<n);i++){
        if(__builtin_popcount(i)<3||!sum[i]) continue;
        C.clear();
        int cnt=0;
        for(int j=0;j<n;j++){
            if(i&(1<<j)) in[j]=0;
            else in[j]=++cnt;
        }
        for(int j=0;j<n;j++){
            for(int k=j+1;k<n;k++){
                if(G[j][k]&&in[j]!=in[k]){
                    C.a[in[j]][in[j]]++,C.a[in[k]][in[k]]++;
                    C.a[in[j]][in[k]]--,C.a[in[k]][in[j]]--;
                }
            }
        }
        ans=(ans+sum[i]*inv(2)%MOD*C.det(cnt))%MOD;
    }
    return ans;
}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(dp, 0, sizeof(dp));
        memset(G, 0, sizeof(G));
        memset(sum, 0, sizeof(sum));
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x-1][y-1]=G[y-1][x-1]=1;
        }
        caldp();
        ll ans=solve();
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2015-11-14 11:32  PlusSeven  阅读(115)  评论(0)    收藏  举报