JZOJ3601. 【广州市选2014】Tree

87 篇文章 0 订阅

题目

下图展示了一种二叉树:
这里写图片描述

这种二叉树的每个叶子节点上都标注了权值,而且具有以下有趣的特性:每个非叶子节点,其左右子树叶子节点的权值之和相等。我们称这种二叉树叫平衡二叉树。

我们将一棵平衡二叉树叶子节点的权值从左到右列出来,假如这个权值序列是另一个序列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、同一棵数的节点权值都可以写成 k2p (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);   
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值