洛谷 P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理+容斥

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82774643

题目描述

四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖怪涌入了幻想乡,扰乱了幻想乡昔日的秩序。但是幻想乡的建制派妖怪(人类)博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡目前面临的种种大问题却给不出合理的解决方案。

风见幽香是幻想乡里少有的意识到了问题严重性的大妖怪。她这次勇敢地站了出来参加幻想乡大选,提出包括在幻想乡边境建墙(并让人类出钱),大力开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺利地当上了幻想乡的大统领。

幽香上台以后,第一项措施就是要修建幻想乡的公路。幻想乡一共有 nn 个城市,之前原来没有任何路。幽香向选民承诺要减税,所以她打算只修 n-1n−1条公路将这些城市连接起来。但是幻想乡有正好 n-1n−1 个建筑公司,每个建筑公司都想在修路地过程中获得一些好处。虽然这些建筑公司在选举前没有给幽香钱,幽香还是打算和他们搞好关系,因为她还指望他们帮她建墙。所以她打算让每个建筑公司都负责一条路来修。

每个建筑公司都告诉了幽香自己有能力负责修建的路是哪些城市之间的。所以幽香打算 n - 1n−1条能够连接幻想乡所有城市的边,然后每条边都交给一个能够负责该边的建筑公司修建,并且每个建筑公司都恰好修建一条边。

幽香现在想要知道一共有多少种可能的方案呢?两个方案不同当且仅当它们要么修的边的集合不同,要么边的分配方式不同。

输入输出格式

输入格式:
第一行包含一个整数 n n ,表示城市个数。

接下来 n 1 n - 1 行,第 i i 行表示 第 i i 个建筑公司可以修建的路的列表:以一个非负数 m i m_i ​ 开头,表示其可以修建条路;接下来有 m i m_i ​​ 对数,每对数表示一条边的两个端点。其中不会出现重复的边,也不会出现自环。

输出格式:
输出一行一个整数,表示所有可能的方案数对 1 0 9 + 7 10^9+7 取模的结果。

输入输出样例

输入样例#1:
4
2 3 2 4 2
5 2 1 3 1 3 2 4 1 4 3
4 2 1 3 2 4 1 4 2
输出样例#1:
17
说明

对于 20 % 20\% 的测试点, n < = 5 n<=5

对于 50 % 50\% 的测试点, n < = 8 n<=8

对于 60 % 60\% 的测试点, n < = 10 n<=10

对于 100 % 100\% 的测试点, n < = 17 n<=17

分析:
显然可以容斥,答案就是 n 1 n-1 个公司建的生成树 - n 2 n-2 个公司建的生成树 + + n 3 n-3 个公司建的生成树。
对于一些公司建的生成树,就是把他们的基尔霍夫矩阵加在一起,然后跑矩阵树。
复杂度是 O ( 2 n n 3 ) O(2^{n}*n^3)

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=20;
const LL mod=1e9+7;

using namespace std;

int n,m,x,y;
LL a[maxn][maxn][maxn],b[maxn][maxn],c[maxn][maxn],ans;

LL power(LL x,LL y)
{
    if (y==1) return x;
    LL c=power(x,y/2);
    c=(c*c)%mod;
    if (y%2) c=(c*x)%mod;
    return c; 
}

LL det()
{
    LL ans=1;
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            c[i][j]=b[i][j];
        }
    }
    for (int i=1;i<=n;i++)
    {
        LL inv=power(c[i][i],mod-2);
        for (int j=i+1;j<=n;j++)
        {
            LL rate=c[j][i]*inv%mod;
            for (int k=i;k<=n;k++)
            {
                c[j][k]=(c[j][k]+mod-rate*c[i][k]%mod)%mod;
            }
        }
        ans=(ans*c[i][i])%mod;
        if (!c[i][i]) break;
    }
    return ans;
}

void dfs(int x,int f)
{
    if (x>n)
    {
        if (f==1) ans=(ans+det())%mod;
             else ans=(ans+mod-det())%mod;
        return;
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            b[i][j]=(b[i][j]+mod+a[x][i][j])%mod;
        }
    }
    dfs(x+1,f);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            b[i][j]=(b[i][j]+mod-a[x][i][j])%mod;
        }
    }
    dfs(x+1,-f);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d",&m);
        for (int j=1;j<=m;j++)
        {
            scanf("%d%d",&x,&y);
            a[i][x][y]--;
            a[i][y][x]--;
            a[i][x][x]++;
            a[i][y][y]++;
        }
    }
    n--;	
    dfs(1,1);
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82774643
今日推荐