题目
下图展示了一种二叉树:
这种二叉树的每个叶子节点上都标注了权值,而且具有以下有趣的特性:每个非叶子节点,其左右子树叶子节点的权值之和相等。我们称这种二叉树叫平衡二叉树。
我们将一棵平衡二叉树叶子节点的权值从左到右列出来,假如这个权值序列是另一个序列A的子序列,我们称这棵平衡二叉树“隐藏”在序列A当中。在本题中,我们称一个序列S2是另一个序列S1的子序列,当且仅当S2可以由S1中删除0个或多个元素,但不改变S1中剩余元素的相对位置获得。
例如,上图中的平衡二叉树隐藏在序列3 4 1 3 1 2 4 4 6中,因为4 1 1 2 4 4是该序列的子序列。
你的任务是对给定的整数序列,寻找当中隐藏的具有最多叶子节点的平衡二叉树。
Data Constraint
n<=1000,1<=ai<=500
题解
我们可以观察发现这种数的一些特性:
1、同一棵数的节点权值都可以写成
k∗2p
(k是常数)。
2、每一个叶子节点都可以整除前面全部叶子节点的权值和。
3、所以叶子节点的权值和是2的次幂。
在这题中,
ai
很小,只有500。所以我们可以枚举k
对于每一个k,我们将所以符合要求的
ai
进行dp,
是
fs
表示当和为s时的最大个数。
code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 1003
using namespace std;
int n,a[N],f[500*N],mx,ans,t[N],z[20],tot;
bool pd(int x)
{
for(int i=0;i<10;i++)
if(z[i]==x)return 1;
return 0;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
z[0]=1;
for(int i=1;i<20;i++)
z[i]=z[i-1]*2;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int k=1;k<=500;k++)
{
tot=0;
for(int i=1;i<=n;i++)
if(a[i]%k==0 && pd(a[i]/k))t[++tot]=a[i]/k;
memset(f,128,sizeof(f));
f[t[1]]=1;
f[0]=0;
mx=t[1];
for(int j=2;j<=tot;j++)
{
for(int i=mx/t[j];i>=0;i--)
f[(i+1)*t[j]]=max(f[(i+1)*t[j]],f[i*t[j]]+1);
mx=(mx/t[j]+1)*t[j];
}
for(int i=0;i<20 && z[i]<=mx;i++)
ans=max(ans,f[z[i]]);
}
printf("%d",ans);
}