[SHOI2016]黑暗前的幻想乡

Description

(n-1) 个边集,从每个边集里选一条边,使得有 (n) 个点的图 (G) 连通,求方案数。

Solution

如果直接把所有边建好矩阵跑 Matrix-tree 的话,可能会有多条边来自同一边集,同时有些边集没有选到。所以强制不选其中 (k) 个边集的边,然后建图,每次都跑 Matrix-tree,再乘上容斥系数 ((-1)^k)。这样就得到了答案。

复杂度 (O(2^nn^3log n))。这样算下来有十多亿,但上界很宽,跑不满。实际上一秒就出来了。

#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 20
#define ll long long

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

ll ans=0,a[N][N];

struct E{
    int u,v;
    inline void readin(){u=read(),v=read();}
    inline void add(){a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;}
    inline void del(){a[u][u]--,a[v][v]--,a[u][v]++,a[v][u]++;}
}sta[N][N*N];

int n,top[N];
ll b[N][N];

const ll Mod=1000000007ll;

ll gs(){
    int S=n-1;
    ll ret=1;
    for(int i=1;i<=S;i++)
        for(int j=1;j<=S;j++)
            b[i][j]=(a[i][j]%Mod+Mod)%Mod;
    for(int i=1;i<=S;i++){
        for(int j=i+1;j<=S;j++)
            while(b[j][i]){
                ll t=b[i][i]/b[j][i];
                for(int k=i;k<=S;k++)
                    b[i][k]=(b[i][k]-t*b[j][k]%Mod+Mod)%Mod;
                swap(b[i],b[j]),ret=-ret;
            }
        ret=(ret*b[i][i]%Mod+Mod)%Mod;
        if(!ret) return 0;
    }
    return ret;
}

void dfs(int st,int op){
    if(st==n) ans=(ans+op*gs()%Mod+Mod)%Mod;
    else{
        for(int i=1;i<=top[st];i++)
            sta[st][i].add();
        dfs(st+1,op);
        for(int i=1;i<=top[st];i++)
            sta[st][i].del();
        dfs(st+1,-op);
    }
}

int main(){
    n=read();
    for(int i=1;i<n;i++){
        top[i]=read();
        for(int j=1;j<=top[i];j++)
            sta[i][j].readin();
    }
    dfs(1,1);
    printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/14348126.html