bzoj 4596: [Shoi2016]黑暗前的幻想乡【容斥原理+矩阵树定理】

真是简单粗暴
把矩阵树定理的运算当成黑箱好了反正我不会
这样我们就可以在O(n^3)的时间内算出一个无向图的生成树个数了
然后题目要求每个工程队选一条路,这里可以考虑容斥原理:全选的方案数-不选工程队1能修的路的方案数-不选工程队2能修的路的方案数……+不选工程队12能修的路的方案数+不选工程队13能修的路的方案数……-不选工程队123能修的路的方案数……
这里直接O(2^(n-1))枚举选择状态即可,然后根据不选的个数奇偶来决定在ans上减或加即可

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=20,mod=1e9+7;
int n,a[N][N];
long long ans;
vector<pair<int,int> >v[N];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
int gaosi(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i][j]<0)
                a[i][j]+=mod;
    long long ans=1,f=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            long long x=a[i][i],y=a[j][i];
            while(y)
            {
                long long t=x/y;
                x%=y;
                swap(x,y);
                for(int k=i;k<=n;k++)
                    a[i][k]=(a[i][k]-a[j][k]*t%mod+mod)%mod;
                for(int k=i;k<=n;k++)
                    swap(a[i][k],a[j][k]);
                f=-f;
            }
        }
        if(a[i][i]==0)
            return 0;
        ans=ans*a[i][i]%mod;
    }
    return f==-1?(mod-ans)%mod:ans;
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int m=read();
        for(int j=1;j<=m;j++)
        {
            int x=read(),y=read();
            v[i].push_back(make_pair(x,y));
        }
    }
    for(int i=0;i<(1<<(n-1));i++)
    {
        int s=n-1,x=i;
        memset(a,0,sizeof(a));
        for(int j=1;j<n;j++,x>>=1)
            if(x&1)
            {
                s--;
                for(int k=0;k<v[j].size();k++)
                    a[v[j][k].first][v[j][k].first]++,a[v[j][k].second][v[j][k].second]++,a[v[j][k].first][v[j][k].second]--,a[v[j][k].second][v[j][k].first]--;
            }
        if(s&1)
            ans=(ans-gaosi(n-1))%mod;
        else
            ans=(ans+gaosi(n-1))%mod;
    }
    printf("%d\n",(ans+mod)%mod);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lokiii/p/9253168.html
今日推荐