2020 GDUT Winter Personal Training Contest I (Div. 2) I - Powers Of Two 题解

原题

在这里插入图片描述

题目大意

给定一个整数n,要求用k个2的幂次方表示

题目分析

啊啊啊啊啊啊啊啊没来得及交啊
想知道一个数怎么拆成二进制的话上网查查快速幂就行,这个不难的
到了这道题的话,因为要凑够k个数,那么简单的分离是不足够的,不过我们可以先来两个剪枝

  1. 当分离出来的数(也就是指数二进制中1)的个数比k还多的时候,那就不能再用k个数去表示n了(见in3)
  2. 当全部数被分成了1还不小于k个数的时候,也不能凑够k个数(见in4)

然后就是怎么分才能分出k个数了,但其实很简单……主要是因为我没看到时间有4s
有一个数学方法,设分完后有n个数, [ l o g 2 n ) ] − 1 [log_2n)]-1 [log2n)]1就是掉的指数,而去掉第一位1之后剩下的数就是掉完指数级别之后要分多少次,比方说 n = 5 = ( 101 ) 2 n=5=(101)_2 n=5=(101)2,那就要掉2层,然后还要再分1次,就得到了5个数
在这里插入图片描述
分出来的就是最下面的5个叶子节点
程序我没有打这个方法……因为没时间了(最后还是慢了点),不过既然研究了出来就放上来说一下吧……

后来我就写了一个很简单的桶(没时间),将一个数分一次之后扔到下一层,然后一直线性扔也能过
因为没有时间(而且看不到限时),在程序中我只做了个小优化,就是一个数全拆之后如果还不大于k,那就把它全拆开了

最后没交上去……在剩60s的时候编译过了,但样例没过,看了一下程序发现减法做反了……果然是数学太差了,连四则运算都不会……最后终于在比赛结束后2min交了上去并且AC了

代码

#include<cstdio>
 
long long ans[60],tot = 0;
int brl[60];
void init()
{
    int cnt = 1;
    for (int i = 0;i < 60;i++)
    {
        ans[i] = cnt;
        cnt <<= 1;
    }
}
int main()
{
    init();
    long long n,k;
    scanf("%lld%lld",&n,&k);
    long long x = n,cnt = 0;
    while (x)
    {
        if (x & 1) 
        {
            brl[cnt]++,tot++;
        }
        cnt += 1;
        x >>= 1;
    }
    if (tot <= k && n >= k)
    {
        int rem = k - tot;
        for (;cnt > 0;cnt--)
            if (brl[cnt]) break;
        while (ans[cnt] - 1 < rem)
        {
            rem -= ans[cnt] - 1;
            brl[cnt]--;
            brl[0] += ans[cnt];
            for (;cnt > 0;cnt--)
                if (brl[cnt]) break;
        }
        while (rem > 0) 
        {
            brl[cnt]--;
            brl[cnt - 1] += 2;
            rem--;
            for (;cnt > 0;cnt--)
                if (brl[cnt]) break;
        }
        printf("YES\n");
        for (int i = 0;i <= cnt;i++)
            while (brl[i]--) printf("%d ",ans[i]);
    }
    else printf("NO");
    return 0;
}

初始化一下2的幂次方防止暴毙(被数论专题折磨的)

接下来应该还会有一题……看今天能不能a过去吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值