CF917D Stranger Trees

CF917D Stranger Trees

题目描述

给定一个树,对于每个\(k=0,1\cdots 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\),则我们有一下变换:

\[\begin{align} \displaystyle ans&=(\prod_{i=1}^k a_i)\sum_{p\in 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_{p\in 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\)的生成树数量。

代码:

没写

posted @ 2019-03-05 14:36  hec0411  阅读(303)  评论(0编辑  收藏  举报