原题
题目大意
给定一个整数n,要求用k个2的幂次方表示
题目分析
啊啊啊啊啊啊啊啊没来得及交啊
想知道一个数怎么拆成二进制的话上网查查快速幂就行,这个不难的
到了这道题的话,因为要凑够k个数,那么简单的分离是不足够的,不过我们可以先来两个剪枝
- 当分离出来的数(也就是指数二进制中1)的个数比k还多的时候,那就不能再用k个数去表示n了(见in3)
- 当全部数被分成了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过去吧