BZOJ 4596: [Shoi2016]黑暗前的幻想乡(容斥+Matrix_Tree)

传送门

解题思路

  看到计数想容斥--\(from\) \(shadowice1984\)大爷。首先求出原图的生成树个数比较容易,直接上矩阵树定理,但这样会多算一点东西,会把\(n-2\)个公司的多算进去,那我们就减掉\(n-2\)个公司的生成树个数,然后发现少算了\(n-3\)的生成树个数...以此类推。所以就容斥一下,然后用矩阵树定理就行了。时间复杂度\(O(2^(n-1)*n^3*log(MOD)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define int long long

using namespace std;
const int MAXN = 18;
const int MOD = 1e9+7;
typedef long long LL;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}

struct Edge{
    int u,v;
}edge[MAXN][MAXN*MAXN/2];

int m[MAXN],n,ans;
int f[MAXN][MAXN];

inline void add(int x,int y){
    f[x][x]++;f[y][y]++;f[x][y]--;f[y][x]--;
}

inline int Matrix_Tree(){
    int t,ret=1;
    for(int i=1;i<n;i++){
        for(int j=i+1;j<n;j++)
            while(f[j][i]){
                t=f[i][i]/f[j][i];
                for(int k=i;k<n;k++) f[i][k]=(f[i][k]-(LL)t*f[j][k]%MOD+MOD)%MOD;
                ret=-ret;swap(f[i],f[j]);
            }
        ret=(LL)ret*f[i][i]%MOD;ret=(ret+MOD)%MOD;
    }
    return (ret+MOD)%MOD;
}

signed main(){
    n=rd();
    for(int i=1;i<n;i++){
        m[i]=rd();
        for(int j=1;j<=m[i];j++)
            edge[i][j].u=rd(),edge[i][j].v=rd();
    }
    for(int i=(1<<(n-1))-1;i;i--){
        memset(f,0,sizeof(f));
        for(int j=1;j<=n;j++)if((1<<(j-1))&i)
            for(int k=1;k<=m[j];k++) 
                add(edge[j][k].u,edge[j][k].v);
        ans+=((n-__builtin_popcount(i))&1)?(Matrix_Tree()):(-Matrix_Tree());
        ans=(ans+MOD)%MOD;
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/sdfzsyq/p/10051617.html