这天,小明在砍竹子,他面前有 n 棵竹子排成一排,一开始第 i 棵竹子的高度为 hi。
他觉得一棵一棵砍太慢了,决定使用魔法来砍竹子。
魔法可以对连续的一段相同高度的竹子使用,假设这一段竹子的高度为 H,那么使用一次魔法可以把这一段竹子的高度都变为 ,其中 ⌊x⌋表示对 x 向下取整。
小明想知道他最少使用多少次魔法可以让所有的竹子的高度都变为 1。
输入格式
第一行为一个正整数 n,表示竹子的棵数。
第二行共 n 个空格分开的正整数 hi,表示每棵竹子的高度。
输出格式
一个整数表示答案。
数据范围
对于 20% 的数据,保证 1≤n≤1000,1≤hi≤10^6。
对于 100% 的数据,保证 1≤n≤2×10^5,1≤hi≤10^18。
输入样例:
6
2 1 4 2 6 7
输出样例:
5
样例解释
其中一种方案:
2 1 4 2 6 7
→ 2 1 4 2 6 2
→ 2 1 4 2 2 2
→ 2 1 1 2 2 2
→ 1 1 1 2 2 2
→ 1 1 1 1 1 1
共需要 5 步完成。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
#include <vector>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 2e5+10;
int n , m ;
LL ans ;
vector < unordered_set<LL> > a(N);
//使用unordered_map可以自动排序,便于使用count函数
LL sqr(LL x){
LL t = sqrt(x);
//由于精度问题进行调整
if((t + 1) * (t + 1) <= x) t = t + 1 ;
else if( t * t > x) t = t - 1 ;
return t;
}
// LL sqr(LL x){
// LL l = 1,r = 1e9;
// while(l < r)
// {
// LL mid = l+r +1>> 1;
// if(mid*mid <= x) l = mid;
// else r = mid - 1;
// }
// return l;
// }
int main()
{
scanf("%d",&n);
a.resize(n+1);
for (int i = 1; i <= n; i ++ ){
LL x;
scanf("%lld", &x);
while(x > 1 ){
//若是前一个竹子砍的过程不出现x高度则多砍一次 若是出现过,则可以与前面的一次砍完
if( !a[i-1].count(x) ) ans ++ ;
a[i].insert(x);
x = sqr(x / 2 + 1);
}
}
cout << ans ;
return 0;
}