zoukankan      html  css  js  c++  java
  • 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)的生成树数量。

    代码:

    没写

  • 相关阅读:
    C#可视化程序设计第三章(3,4)
    "Can’t be opened because Apple cannot check it for malicious software" 解决方案
    Mac系统.DS_Store文件导致IOError: [Errno 20] Not a directory:解决方案
    读书笔记 《局外人》
    Chrome 67之后无法离线安装插件
    函数和方法的区别
    github|webstorm
    webstorm
    Markdown
    github
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10476650.html
Copyright © 2011-2022 走看看