zoukankan      html  css  js  c++  java
  • CF917D Stranger Trees

    CXX.CF917D Stranger Trees

    这里是本题的DP解法。矩阵树定理解法详见矩阵树定理学习笔记中重题III.TopCoder13369-TreeDistance

    首先,一个基础结论是,如果一张 \(n\) 个点的图,被连成一棵森林,则继续加边连成一棵树的方案数是 \(n^{k-2}\prod\limits_{i=1}^kc_i\),其中森林中共有 \(k\) 棵树,第 \(i\) 棵树的大小是 \(c_i\)

    然后,考虑二项式反演。设 \(g_i\) 是至多有 \(i\) 条边在原树上的方案数。二项式反演一下即可得到恰好有 \(i\) 条边在原树上的方案数。则问题被转化为求出 \(g\) 数组。\(g\) 数组中 \(n^{k-2}\) 的部分是可以提出来统一计算的。考虑使用DP来求出 \(g\) 的另一部分。设 \(f_{i,j,k}\) 表示 \(i\) 的子树中被截成 \(j\) 棵树,且 \(i\) 节点自身所在树大小为 \(k\) 的权值(不包括 \(i\) 自身的树)和。合并节点 \(i\) 和儿子 \(i'\) 时,只需对二者处于同一连通块或是不同连通块进行树上背包分开转移即可。

    时间复杂度 \(O(n^4)\),是树上背包复杂度。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int n,f[110][110][110],g[110],sz[110],h[110][110],fac[110],inv[110];
    vector<int>v[110];
    int ksm(int x,int y=mod-2){
    	int z=1;
    	for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
    	return z;
    }
    void dfs(int x,int fa){
    	sz[x]=1,f[x][1][1]=1;
    	for(auto y:v[x]){
    		if(y==fa)continue;
    		dfs(y,x);
    		for(int i=1;i<=sz[x];i++)for(int I=1;I<=sz[y];I++)for(int j=1;j<=sz[x];j++)for(int J=1;J<=sz[y];J++){
    			(h[i+I][j]+=1ll*f[x][i][j]*f[y][I][J]%mod*J%mod)%=mod;
    			(h[i+I-1][j+J]+=1ll*f[x][i][j]*f[y][I][J]%mod)%=mod;
    		}
    		for(int i=1;i<=sz[x]+sz[y];i++)for(int j=1;j<=sz[x]+sz[y];j++)f[x][i][j]=h[i][j],h[i][j]=0;
    		sz[x]+=sz[y];
    	}
    }
    int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
    int main(){
    	scanf("%d",&n);
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
    	fac[0]=1;for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
    	inv[n]=ksm(fac[n]);for(int i=n-1;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    	dfs(1,0);
    	for(int i=0,k=ksm(n);i<n;i++,k=1ll*k*n%mod){
    		for(int j=1;j<=n;j++)(g[i]+=1ll*f[1][i+1][j]*j%mod)%=mod;
    		g[i]=1ll*g[i]*k%mod;
    	}
    	for(int i=0;i<n;i++)for(int j=0;j<i;j++)(g[i]+=mod-1ll*g[j]*C(n-j-1,i-j)%mod)%=mod;
    	for(int i=0;i<n;i++)printf("%d ",g[n-i-1]);
    	return 0;
    }
    

  • 相关阅读:
    02 _ 该如何选择消息队列
    封装、抽象、继承、多态分别可以解决哪些编程问题?
    04 _ 理论一:当谈论面向对象的时候,我们到底在谈论什么?
    03 _ 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
    接口使用
    结构体和方法
    通道的高级玩法
    通道的基本操作
    极客时间 mp3提取
    iOS多线程中的单例
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601370.html
Copyright © 2011-2022 走看看