[Code Festival 2017 qual A-F] Squeezing Slimes

Atcoder传送门

题目大意

给你一个长度为 N N 的序列a1,a2,a3,....,aN, 元素和 Ni=1ai=M ∑ i = 1 N a i = M

开始时你有一个长度为 M M 的全1序列。 现在你可以指定一段长度为偶数的序列, 使其中相邻的一组元素合并起来。例如 1,5,9,3 1 , 5 , 9 , 3 可以合并为 6,12 6 , 12 3,9,5,5 3 , 9 , 5 , 5 可以合并为 12,10 12 , 10

问最少需要多少次操作, 能够得到给定的序列。

输入输出格式

输入格式

第一行一个正整数 N N

第二行N个正整数表示 a1,a2,a3,...,aN a 1 , a 2 , a 3 , . . . , a N

输出格式

一行一个正整数, 表示需要的最少操作次数。

输入输出样例

输入样例#1
2
3 3
输出样例#1
2
输入样例#2
4
2 1 2 2
输出样例#2
2
输入样例#3
1
1
输出样例#3
0
输入样例#4
10
3 1 4 1 5 9 2 6 5 3
输出样例#4
10

数据范围

  • 1N105 1 ≤ N ≤ 10 5
  • 1ai109 1 ≤ a i ≤ 10 9

解题分析

我们考虑一次操作需要尽量覆盖更大的区间。

设当前点需要操作次数为 x x ,下一个值的大小为y

z=log2(y) z = ⌊ l o g 2 ( y ) ⌋ 。如果 2z=y 2 z = y , 显然我们需要 z z 次操作就可以得到y。如果 x<y x < y , 那么我们需要新加 yx y − x 次操作。如果 x>y x > y , 那么我们无法再将多的一部分操作继续覆盖下一部分区间, 所以这两种情况 x x 都需要赋值为y

如果 2zy 2 z ≠ y ,那么我们需要多一次合并操作, 打个标记特判即可。

总复杂度 O(N) O ( N )

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
#define ll long long
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int main(void)
{
    int num, buf, mul; in(num);
    bool merge = false; ll ans = 0, cut = 0;
    W (num--)
    {
        in(buf); mul = std::log2(buf); merge = false;
        if((1 << mul) != buf) merge = true;
        if(cut < mul) ans += mul - cut;
        if(cut > mul) merge = false;//如果已经操作的次数大于了需要的次数, 也不需要多一次合并的花费
        cut = mul; if(merge) ++cut, ++ans;
    }
    printf("%lld", ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值