http://acm.hdu.edu.cn/showproblem.php?pid=2292
题意:1-n个节点,题目给出了完全二叉树的定义(这个定义似乎有歧义,此题以题目描述为准),且要保持最小堆性质(根节点小于左右子树内的任意元素),问有多少种不同组合
解法:dp,dp[n]表示n个元素的合法排列数量。一共n个节点,左子树有a个节点,则右子树有n-1-a个节点,dp[n]=C(n-1,a)*dp[a]*dp[n-1-a],其中a可以轻易算出。
公式解释:除去根节点,在剩下的n-1个元素中取a个,这a个元素的合法排列有dp[a]种,剩下n-1-a个节点的合法排列有dp[n-1-a]种。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std ;
typedef __int64 ll ;
ll c[1005][1005],n,m,dp[1005] ;
ll cal(int s)
{
ll temp=s-1 ;
ll cnt=2 ;
ll ans=0 ;
while(temp-cnt>0)
{
ans+=cnt/2 ;
temp-=cnt ;
cnt*=2 ;
}
if(temp>=cnt/2)ans+=cnt/2 ;
else ans+=temp ;
return ans ;
}
int main()
{
int t ;
scanf("%d",&t) ;
while(t--)
{
scanf("%I64d%I64d",&n,&m) ;
memset(c,0,sizeof(c)) ;
for(int i=1 ;i<1001 ;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])%m ;
}
memset(dp,0,sizeof(dp)) ;
dp[0]=dp[1]=1 ;
for(int i=2 ;i<=n ;i++)
{
ll a=cal(i) ;
ll b=i-1-a ;
dp[i]=c[i-1][a]*dp[a]%m*dp[b]%m ;
}
printf("%I64d\n",dp[n]%m) ;
}
return 0 ;
}
时间: 2024-10-13 22:12:26