CF917D Stranger Trees 矩阵树定理+高斯消元

题目大意:
给你 n 个点的完全图和一棵 n 个点的树 T ,求有多少棵生成树,满足有 k 条边在树 T 上。
对于任意 k [ 1 , n 1 ] 都要回答询问。

分析:
生成树计数可以想到矩阵树定理,但是显然矩阵树跑出来的值是边权的乘积的和,不能统计该树上的边与给出的树的重合边数。
考虑把一条边看做一个多项式,如果这条边在原树上,这条边的权值看作是 x 1 ,否则看做 1 。于是一棵树的总权值就是 x k ,其中 k 是与原树重合的边的数目。然后我们可以把这些多项式求和,用矩阵树算出来,总和就是一个多项式,其中 x k 的系数表示有 k 条边与原树相同。
我们不能把多项式带人进去算,但是我们知道最终的多项式的次数是 n 1 的。所以我们可以取 n 个数代替 x 去求行列式,因为是要求出系数,所以直接暴力高斯消元插值就好了。复杂度是 O ( n 4 ) 的。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long

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

using namespace std;

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

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(int n)
{
    LL ans=1;
    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;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        a[x][y]=1;
        a[y][x]=1;
    }
    for (int k=1;k<=n;k++)
    {
        memset(c,0,sizeof(c));
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)
            {
                if (i!=j)
                {
                    if (a[i][j]) c[i][j]=mod-k,c[i][i]+=k;
                            else c[i][j]=mod-1,c[i][i]+=1;
                } 
            }
        }       
        b[k]=det(n-1);
    }

    for (int i=1;i<=n;i++)
    {
        c[i][1]=1;
        for (int j=2;j<=n;j++) c[i][j]=(c[i][j-1]*i)%mod;
    }
    for (int i=1;i<=n;i++)
    {
        LL inv=power(c[i][i],mod-2);
        for (int j=1;j<=n;j++)
        {
            if (i==j) continue;
            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;
            }
            b[j]=(b[j]+mod-rate*b[i]%mod)%mod;
        }
    }
    for (int i=1;i<=n;i++)
    {
        b[i]=b[i]*power(c[i][i],mod-2)%mod;
        printf("%lld ",b[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81586276