题意
你想要为众蛇建造一座神殿。神殿将建在群山之中,可以将其视作n个块,其中第i个的高度为hi。神殿会建造在连续若干个块上,要求这些块的高度从1开始逐渐增加到某个高度,然后再逐渐减小到1,即,高度是形如1,2,3, ... ,x−1,x,x−1,x−2,...,1的序列。除了要建造神殿的块之外,其他块的高度都应该是0,以便人们从左右两侧看到神殿。
为了达成这一目标,你可以降低块的高度。一次操作中,你可以选择一块,并令其高度减小1。请求出最少需要进行多少次操作。
数据
输入的第一行包含一个整数 T,代表测试数据的组数。接下来是 T 组数据。每组数据的第一行包含一个整数n。接下来一行包含n个整数,其中第i个代表hi。
对于每组数据,输出一行,包含一个整数,代表最少需要的操作次数。
1<=T<=10, 2<=n<=1e5, 1<= hi<=1e9
输入
3
3
1 2 1
4
1 1 2 1
5
1 2 6 2 1
输出
0
1
3
说明
在第一组数据中,整座山已经符合要求。
在第二组数据中,将第 1 块的高度减小为 0,便可以在后 3 块上建造神殿。
在第三组数据中,可以将第 3 块的高度减小为 3。
思路(摘自Wannafly Union)
这道题的本质是求我们能留下来的最大的塔有多大,然后用总块数-用掉的块数就可以求出答案。我们考虑以每个点为中心,塔的左边能造多高。假设i号点最高能建h,那么i+1号点最高能建min(h+1,自身高度)。同理可求只考虑右边的情况。最后求个最值就可以了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int a[maxn], l[maxn], r[maxn];
int main(void)
{
int t, n;
cin >> t;
while(t--)
{
memset(a, 0, sizeof(a));
memset(l, 0, sizeof(l));
memset(r, 0, sizeof(r));
scanf("%d", &n);
ll sum = 0;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), sum += a[i];
for(int i = 1; i <= n; i++)
l[i] = min(a[i], l[i-1]+1);
for(int i = n; i >= 1; i--)
r[i] = min(a[i], r[i+1]+1);
ll ans = 0;
for(int i = 1; i <= n; i++)
ans = max(ans, (ll)min(l[i], r[i]));
printf("%lld\n", sum-(1+ans-1)*(ans-1)-ans);
}
return 0;
}