http://acm.hdu.edu.cn/showproblem.php?pid=4359
比赛的时候就悲剧了,当时我就卡在1006题上去了,然后这题也没心情去搞这题,估计当时我弄的话也搞不出来,表示关于这种 2叉树计数 做的题目还是很少,以后要加强这方面的练习,哭,看的解题报告才弄出来的,无语中……
贴下官方的解题报告:
Author: smxrwzdx@UESTC Brightroar
水题一枚:首先左子树和<右子树和等价于左子树最大值<右子树最大值。根节点不受限制。所以N个元素里面任选。还剩N-1个。
除根节点外最大的元素必然在根节点的右子树上。一脸dp[N][D]的样子然后假设深度D-1在右子树上,依次枚举左子树元素个数和深度 D-1的所有情况(此处要用前缀和优化)DP即可,深度D-1在左子树上同理可得。(枚举时注意根节点和除根节点外最大数已被选,还有深度D-1同时出现在两颗子树上的情况算了两次要剪掉一次) 由于有5000组数据所以要用预处理。注意合适取模,姿势不优越的别爆long long了。。。。。。。。。。
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=370;
const int mod=1000000007;
ll c[maxn][maxn],dp[maxn][maxn],sum[maxn][maxn];
void solve()
{
c[0][0]=c[1][0]=c[1][1]=1;
for(int i=2;i<maxn;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
dp[1][1]=1;
for(int i=1;i<=360;i++)
sum[1][i]=1;
for(int i=2;i<=360;i++)
{
for(int j=1;j<=i;j++)
{
dp[i][j]=2*dp[i-1][j-1]%mod*i%mod;
for(int k=1;k<i-1;k++)
{
dp[i][j]=(dp[i][j]+i*c[i-2][k]%mod*dp[k][j-1]%mod*sum[i-1-k][j-2]%mod)%mod;
dp[i][j]=(dp[i][j]+i*c[i-2][k]%mod*sum[k][j-2]%mod*dp[i-1-k][j-1]%mod)%mod;
dp[i][j]=(dp[i][j]+i*c[i-2][k]%mod*dp[k][j-1]%mod*dp[i-1-k][j-1]%mod)%mod;
}
sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
}
for(int j=i+1;j<=360;j++)
sum[i][j]=sum[i][i];
}
}
int main()
{
int ca,cas=1,a,b;
scanf("%d",&ca);
solve();
while(ca--)
{
scanf("%d%d",&a,&b);
printf("Case #%d: %I64d\n",cas++,dp[a][b]);
}
return 0;
}
/*
327 175
Case #1: 69258578
*/
如果你不能理解,下面的一篇博客用的记忆化dp的方式,或许就可以理解了, http://www.darkswordzone.com/?p=1288