Codeforces 954H Path Counting 【DP计数】*

55 篇文章 2 订阅
46 篇文章 0 订阅
这篇博客介绍了如何解决Codeforces 954H题目,即计算一棵n层树中特定长度简单路径的数量。通过定义状态f[i][j]表示在第i层节点向下走j步的方案数,以及g[i][j]表示在第i层节点第一步向上走共走j步的方案数,利用动态规划求解。为了避免内存错误,通过对g的转移方式进行调整,可以在f数组上直接修改,实现空间优化。
摘要由CSDN通过智能技术生成

Codeforces 954H Path Counting


LINK


题目大意:给你一棵n层的树,第i层的每个节点有 a [ i ] a[i] a[i]个儿子节点,然后问你树上的简单路径中长度在 1   n ∗ 2 − 2 1~n*2-2 1 n22之间的每个有多少条


因为直接计算过每个节点的路径并不好算
所以可以算一算从每个节点出发的路径的个数
f [ i ] [ j ] f[i][j] f[i][j]表示对于在i层的1个节点,向下走行走j步的方案数
g [ i ] [ j ] g[i][j] g[i][j]表示对于在i层的1个节点,第一步向上行走共走j步的方案数

然后DP式子比较显然
f [ i ] [ j ] = a [ i ] ∗ f [ i + 1 ] [ j − 1 ] f[i][j]=a[i]*f[i+1][j-1] f[i][j]=a[i]f[i+1][j1]
g [ i ] [ j ] = g [ i − 1 ] [ j − 1 ] + [ 2 ≤ j ] ∗ f [ i ] [ j − 2 ] g[i][j]=g[i-1][j-1]+[2\le j]*f[i][j-2] g[i][j]=g[i1][j1]+[2j]f[i][j2]
然后这样每条路径会在计算的时候被计算两次,所以乘上2的逆元就好了

但是这样会 M L E MLE MLE,就很完蛋

然后我们发现g式子DP的时候f只会用到i相等,j比他小的
又因为g的DP式子中是i从小到大来转移的
所以直接在f上进行修改,i从小到大,j从大到小,然后就不会卡空间了

也可以滚动,不过要处理一堆东西,懒得写


#include<bits/stdc++.h>
using namespace std;
#define N 5010
#define Mod 1000000007
#define inv2 500000004
int f[N][N<<1];
int n,a[N],s[N];
int ans[N<<1]={0};
int add(int a,int b){return (a+b)%Mod;}
int mul(int a,int b){return 1ll*a*b%Mod;}
int main(){
	scanf("%d",&n);
	for(int i=1;i<n;i++)scanf("%d",&a[i]);
	s[1]=1;for(int i=2;i<=n;i++)s[i]=mul(s[i-1],a[i-1]);
	for(int i=1;i<=n;i++)f[i][0]=1;
	for(int i=n-1;i>=1;i--)
		for(int k=1;k<=n-1;k++){
			f[i][k]=mul(a[i],f[i+1][k-1]);
			ans[k]=add(ans[k],mul(s[i],f[i][k]));
		}
	for(int i=1;i<=n;i++)f[1][i]=0;
	for(int i=2;i<=n;i++)
		for(int k=n*2-2;k>=1;k--){
			f[i][k]=f[i-1][k-1];
			if(k>=2)f[i][k]=add(f[i][k],mul(a[i-1]-1,f[i][k-2]));
			ans[k]=add(ans[k],mul(s[i],f[i][k]));
		}
	for(int i=1;i<=n*2-2;i++)printf("%d ",mul(ans[i],inv2));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值