Atcoder传送门
题目大意
给你一个长度为 N N 的序列, 元素和 ∑Ni=1ai=M ∑ i = 1 N a i = M 。
开始时你有一个长度为 M M 的全序列。 现在你可以指定一段长度为偶数的序列, 使其中相邻的一组元素合并起来。例如 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 。
第二行个正整数表示 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
数据范围
- 1≤N≤105 1 ≤ N ≤ 10 5
- 1≤ai≤109 1 ≤ a i ≤ 10 9
解题分析
我们考虑一次操作需要尽量覆盖更大的区间。
设当前点需要操作次数为 x x ,下一个值的大小为。
设 z=⌊log2(y)⌋ z = ⌊ l o g 2 ( y ) ⌋ 。如果 2z=y 2 z = y , 显然我们需要 z z 次操作就可以得到。如果 x<y x < y , 那么我们需要新加 y−x y − x 次操作。如果 x>y x > y , 那么我们无法再将多的一部分操作继续覆盖下一部分区间, 所以这两种情况 x x 都需要赋值为。
如果 2z≠y 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);
}