CF917D-Stranger Trees【矩阵树定理,高斯消元】

正题

题目链接:https://www.luogu.com.cn/problem/CF917D


题目大意

在这里插入图片描述
给出 n n n个点的一棵树,对于每个 k k k求有多少个 n n n个点的树满足与给出的树恰好有 k k k条边重合。


解题思路

矩阵树有一个统计所有树边权和的和用法,就是把变量变成一个形如 w x + 1 wx+1 wx+1的多项式,这样一次项系数的值就表示了固定选择一条边的 w w w时其他边的方案数之和。

这题我们可以同理,对于在给出数上的边是 x x x,而其他就是 1 1 1。那么最后询问 x k x^k xk的系数就是答案了。

如果暴力套 NTT \text{NTT} NTT不仅麻烦,而且跑的很慢过不了本题,考虑另一种求系数的方法。

我们假设答案是一个形如 F ( x ) = ∑ i = 0 n − 1 a i x i F(x)=\sum_{i=0}^{n-1}a_ix^i F(x)=i=0n1aixi n n n次项式,那么我们如果把 n n n x x x的值直接带入求出 F F F,然后用待定系数法的话我们就可以列出 n n n个方程从而解出这个 n n n项式的每一个系数。

时间复杂度 O ( n 4 ) O(n^4) O(n4)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=110,P=1e9+7;
ll n,x[N],y[N];
ll power(ll x,ll b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*x%P;
        x=x*x%P;b>>=1;
    }
    return ans;
}
namespace Guass{
    ll a[N][N],b[N];
    void solve(){
        for(ll i=1;i<=n;i++){
            ll z=i;
            for(ll j=i;j<=n;j++)
                if(a[j][i]){z=j;break;}
            swap(a[i],a[z]);swap(b[i],b[z]);
            ll inv=power(a[i][i],P-2);
            for(ll j=i;j<=n;j++)
                a[i][j]=a[i][j]*inv%P;
            b[i]=b[i]*inv%P;
            for(ll j=i+1;j<=n;j++){
                ll rate=P-a[j][i];
                for(ll k=i;k<=n;k++)
                    (a[j][k]+=rate*a[i][k]%P)%=P;
                (b[j]+=rate*b[i]%P)%=P;
            }
        }
        for(ll i=n;i>=1;i--){
            for(ll j=i+1;j<=n;j++)
                (b[i]+=P-b[j]*a[i][j]%P)%=P;
        }
        return;
    }
}
namespace Matrix{
    ll a[N][N];
    ll det(){
        ll f=1,ans=1;
        for(ll i=1;i<n;i++){
            ll z=i;
            for(ll j=i;j<n;j++)
                if(a[j][i]){
                    if(j!=i)f=-f;
                    z=j; break;
                }
            swap(a[i],a[z]);
            ll inv=power(a[i][i],P-2);
            ans=ans*a[i][i]%P;
            for(ll j=i;j<n;j++)
                a[i][j]=a[i][j]*inv%P;
            for(ll j=i+1;j<n;j++){
                ll rate=P-a[j][i];
                for(ll k=i;k<n;k++)
                    (a[j][k]+=rate*a[i][k]%P)%=P;
            }
        }
        return ans*f;
    }
    void solve(ll w){
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++)
                a[i][j]=P-1;
        for(ll i=1;i<=n;i++)a[i][i]=n-1;
        for(ll i=1;i<n;i++){
            a[x[i]][x[i]]+=w-1;
            a[y[i]][y[i]]+=w-1;
            a[x[i]][y[i]]=P-w;
            a[y[i]][x[i]]=P-w;
        }
        Guass::b[w]=det();
        for(ll i=1,p=1;i<=n;i++,p=p*w%P)
            Guass::a[w][i]=p;
        return;
    }
}
signed main(){
    scanf("%lld",&n);
    for(ll i=1;i<n;i++)
        scanf("%lld%lld",&x[i],&y[i]);
    for(ll i=1;i<=n;i++)Matrix::solve(i);
    Guass::solve();
    for(ll i=1;i<=n;i++)
        printf("%lld ",Guass::b[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值