[Shoi2016] bzoj 4596 黑暗前的幻想乡 - 容斥 - 矩阵树定理 - 学习笔记II

和小星星那个题一样,随便上一波容斥矩阵树即可。
矩阵树定理求的是边权乘积之和,所以可以做形如“恰好有k条黑边”的树计数(跑差值即可)等等。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<vector>
#define fir first
#define sec second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define mod 1000000007
#define inv(x) fast_pow(x,mod-2)
#define N 20
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
int n,g[N][N];
inline int fast_pow(int x,int k)
{
    int ans=1;
    for(;k;x=(lint)x*x%mod,k>>=1)
        if(k&1) ans=(lint)ans*x%mod;
    return ans;
}
inline int calc(int n)
{
    int det=1,v,t;
//  for(int i=1;i<=n;i++,cout ln)
//      for(int j=1;j<=n;j++) cout<<g[i][j]<<" ";
    rep(i,1,n)
    {
        v=inv(g[i][i]),det=(lint)det*v%mod;
        rep(j,i,n) g[i][j]=(lint)g[i][j]*v%mod;
        rep(j,1,n) if((i^j)&&(t=g[j][i]))
            rep(k,i,n) g[j][k]=(g[j][k]-(lint)t*g[i][k]%mod+mod)%mod;
    }
    return inv(det);
}
bool cb[N];vector<pii> e[N];
inline int solve(int S)
{
    for(int i=1;i<n;i++) cb[i]=true;
    for(int i=0;i<n-1;i++) if((S>>i)&1) cb[i+1]=false;
    memset(g,0,sizeof(g));
    for(int i=1;i<n;i++) if(cb[i])
        for(int j=0,x,y;j<(int)e[i].size();j++)
            x=e[i][j].fir,y=e[i][j].sec,g[x][y]--,g[y][x]--,g[x][x]++,g[y][y]++;
    rep(i,1,n) rep(j,1,n) (g[i][j]+=mod)%=mod;return calc(n-1);
}
inline int gsgn(int x,int y)
{
    int c=0;
    for(int i=0;i<n-1;i++)
        if((x>>i)&1) c^=1;
    return c?(mod-y)%mod:y;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int c;scanf("%d",&c);
        while(c--)
        {
            int x,y;scanf("%d%d",&x,&y);
            e[i].push_back(make_pair(x,y));
        }
    }
    int all=(1<<(n-1))-1,ans=0;
    rep(i,0,all) (ans+=gsgn(i,solve(i)))%=mod;
    return !printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/mys_c_k/article/details/79979610