P4336 [SHOI2016]黑暗前的幻想乡

传送门

答案=没有限制的方案数 - (限制1公司没修的方案 ∪ 限制2公司没修的方案 ∪ 限制3公司没修的方案 ∪...∪ 限制n-1公司没修的方案)

矩阵树计算方案数,然后容斥

根据容斥原理

(限制1公司没修的方案 ∪ 限制2公司没修的方案 ∪ 限制3公司没修的方案 ∪...∪ 限制n-1公司没修的方案)

=(所有限制一个公司没修的方案-所有限制两个公司没修的方案+所有限制三个公司没修的方案-所有限制四个公司没修的方案....)

那么枚举数的二进制表示每个公司的参加情况然后计算贡献

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int mo=1e9+7;
int n;
ll G[27][27];
struct edge{
    int x,y;
};
vector <edge> V[27];
int Gauss()
{
    ll ans=1;
    for(int i=1;i<n;i++)
    {
        for(int j=i+1;j<n;j++)
            while(G[j][i])
            {
                ll t=G[i][i]/G[j][i];
                for(int k=i;k<=n;k++)
                    G[i][k]=(G[i][k] - t*G[j][k])%mo;
                swap(G[i],G[j]); ans=-ans;
            }
        ans=ans*G[i][i]%mo;
        if(!ans) return 0;
    }
    return (ans+mo)%mo;
}
int main()
{
    n=read(); int a,x,y;
    for(int i=1;i<n;i++)
    {
        a=read();
        for(int j=1;j<=a;j++)
        {
            x=read(),y=read();
            V[i].push_back((edge){x,y});
        }
    }
    int ans=0,mx=(1<<n-1)-1;
    for(int i=0;i<=mx;i++)
    {
        int cnt=0; memset(G,0,sizeof(G));
        for(int j=1;j<n;j++)
            if((i>>j-1)&1)
            {
                for(int k=V[j].size()-1;k>=0;k--)
                {
                    x=V[j][k].x,y=V[j][k].y;
                    G[x][y]--; G[y][x]--;
                    G[x][x]++; G[y][y]++;
                }
                cnt++;
            }
        if((n-cnt)&1) ans=(ans+Gauss())%mo;
        else ans=(ans-Gauss()+mo)%mo;
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2019-03-30 15:53  LLTYYC  阅读(133)  评论(0编辑  收藏  举报