CF917D Stranger Trees

CF917D Stranger Trees

题目描述

给定一个树,对于每个(k=0,1cdots n-1),问有多少个生成树与给定树有(k)条边重合。

矩阵树定理+高斯消元

我们答案为(f_k)。假设我们呢将原树上的边权设为(x),其他的边权设为(1),那么我们做一次矩阵树定理求出来的东西就是(displaystyle sum_{i=0}^{n-1}f_i x^i)。于是我们找(n)个不同的(x),然后高斯消元就行了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define N 105

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n;
ll a[N][N],d[N];
ll w[N][N];
ll c[N][N],ans[N];
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

void Gauss(ll a[N][N],int n) {
    for(int i=1;i<=n;i++) {
        for(int j=i+1;j<=n;j++) {
            ll t=ksm(a[i][i],mod-2)*a[j][i]%mod;
            for(int k=i;k<=n+1;k++) a[j][k]=(a[j][k]-t*a[i][k]%mod+mod)%mod;
        }
    }
}

int main() {
    
    n=Get();
    for(int i=1;i<n;i++) {
        int x=Get(),y=Get();
        a[x][y]=a[y][x]=1;
        d[x]++,d[y]++;
    }
    
    for(int v=1;v<=n;v++) {
        memset(w,0,sizeof(w));
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                if(i==j) w[i][j]=d[i]*v+n-1-d[i];
                else if(a[i][j]) w[i][j]=mod-v;
                else w[i][j]=mod-1;
            }
        }
        Gauss(w,n-1);
        c[v][n+1]=1;
        for(int i=1;i<n;i++) c[v][n+1]=c[v][n+1]*w[i][i]%mod;
        ll now=1;
        for(int i=1;i<=n;i++,now=now*v%mod) c[v][i]=now;
    }
    
    Gauss(c,n);
    for(int i=n;i>=1;i--) {
        for(int j=i+1;j<=n;j++) {
            c[i][n+1]=(c[i][n+1]-ans[j]*c[i][j]%mod+mod)%mod;
        }
        ans[i]=ksm(c[i][i],mod-2)*c[i][n+1]%mod;
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
    return 0;
}

容斥原理+prufer序列

我们的原理就是选定(k)个边与原树重合,这样我们将原树分成了(n-k)个联通块。我们求出此时的生成树方案树,表示至少(k)条边重合的方案树,然后容斥就好了。

具体求法如下:

给定一颗森林,每个联通块的大小是(a_i),那么这个森林的生成树方案树我们也可以用(prufer)序列来求。将每个连通块视作点,设第(i)个块出现的次数为(t_i),则一个序列的答案为(prod a_i^{t_i})

我们设所有(prufer)序列为(P),则我们有一下变换:

[egin{align} displaystyle ans&=(prod_{i=1}^k a_i)sum_{pin P}prod_{i=1}^{k-2}a_{p_i}\ &=(prod_{i=1}^k a_i)prod_{i=1}^{k-2}sum_{j=1}^ka_j\ &=(prod_{i=1}^k a_i)prod_{i=1}^{k-2}n^{k-2}\ end{align} ]

(sum_{pin P}prod_{i=1}^{k-2}a_{p_i}=prod_{i=1}^{k-2}sum_{j=1}^ka_j)就是一个经典的和式变换。

得到上述的结论过后,我们就可以树形(DP)了。设(f_{i,j,k})表示已(i)为根的子树中,分成了(j)个连通块,(i)所在的连通块的大小为(k)的生成树数量。

代码:

没写

原文地址:https://www.cnblogs.com/hchhch233/p/10476650.html