[BZOJ4596] Utopia before dark (SHOI2016) - matrix tree theorem + inclusion exclusion principle

Test address: Dreamland before dark
Method: this problem needs to use matrix tree theorem + inclusion exclusion principle.
We find that the data range is very small, and this problem is also a counting problem, which inspires us to use the principle of inclusion and exclusion. Then the answer is: the number of schemes selected arbitrarily - the number of schemes not selected by one company + the number of schemes not selected by two companies and so on. Then we only need to enumerate the sets, and then use the matrix tree theorem to calculate the corresponding scheme number. The total time complexity is O(2n − 1(n − 1)3)O(2n − 1(n − 1) 3). Although it seems impossible, if the Gaussian elimination is well written, it is far from the upper bound, so we can use this problem.
Here is my code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
int n,m[21],x[21][410],y[21][410];
ll M[21][21];

void init()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d",&m[i]);
        for(int j=1;j<=m[i];j++)
            scanf("%d%d",&x[i][j],&y[i][j]);
    }
}

ll power(ll a,ll b)
{
    ll s=1,ss=a;
    while(b)
    {
        if (b&1) s=(s*ss)%mod;
        ss=(ss*ss)%mod;b>>=1;
    }
    return s;
}

ll gauss(int n)
{
    ll ret=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
            if (M[j][i])
            {
                for(int k=i;k<=n;k++)
                    swap(M[i][k],M[j][k]);
                break;
            }
        if (!M[i][i]) return 0;
        ll inv=power(M[i][i],mod-2);
        for(int j=i+1;j<=n;j++)
        {
            for(int k=i+1;k<=n;k++)
            {
                M[j][k]-=M[j][i]*inv%mod*M[i][k]%mod;
                M[j][k]=(M[j][k]+mod)%mod;
            }
            M[j][i]=0;
        }
        ret=ret*M[i][i]%mod;
    }
    return ret;
}

void work()
{
    ll ans=0;
    for(int i=1;i<(1<<(n-1));i++)
    {
        int j=0,p=i,tot=0;
        memset(M,0,sizeof(M));
        while(p)
        {
            j++;
            if (p&1)
            {
                tot++;
                for(int k=1;k<=m[j];k++)
                {
                    int X=x[j][k],Y=y[j][k];
                    M[X][Y]--,M[Y][X]--,M[X][X]++,M[Y][Y]++;
                }
            }
            p>>=1;
        }
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if (M[j][k]<0) M[j][k]+=mod;
        if (tot%2==1) ans=(ans+gauss(n-1))%mod;
        else ans=(ans-gauss(n-1)+mod)%mod;
    }
    if (n%2==0) printf("%lld",ans);
    else printf("%lld",mod-ans);
}

int main()
{
    init();
    work();

    return 0;
}

Posted by ghqwerty on Wed, 01 Apr 2020 12:43:50 -0700