题意
一个长度为n的序列h[i]表示第i个位置的数不能超过h[i]。每个数和相邻的数的差只能为-10,0或+10。
现在可以允许一个位置忽略限制,求数列中所有数的和最大为多少。
思路
算出没有忽略限制时所有位置的最大值a[i]。
枚举忽略限制的点。
假如一个点忽略限制能够增大答案,那么a[i]==h[i],否则即使h[i]变为无穷大他也受左右两边的限制而无法超过a[i]。
然后考虑忽略限制之后a[i]的改变。
发现所有a[i]只会增加而不可能减小,所以h[i]变化的影响范围是从i出发向左右两边延伸,到a[j]==h[j]的第一个j时停止。
假设所有h[i]==a[i]的点把序列分割成几个小段,那每个小段只会被访问O(1)次,所以暴力修改O(n)。
赛中胡结论失败。好题。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, inf = 1e9 + 10010;
int n, h[N], r[N], l[N];
LL sum, ans;
void solve(int pos)
{
int pre = 0, suc = n + 1;
for (int i = pos - 1; i >= 1; -- i)
if (h[i] == r[i]){
pre = i;
break;
}
for (int i = pos + 1; i <= n; ++ i)
if (h[i] == r[i]){
suc = i;
break;
}
int tmp = h[pos]; h[pos] = inf;
LL add = 0;
l[pre] = r[pre]; l[suc] = r[suc];
for (int i = pre + 1; i < suc; ++ i)
l[i] = min(h[i], l[i-1] + 10);
for (int i = suc - 1; i > pre; -- i){
l[i] = min(l[i], l[i+1] + 10);
add += l[i] - r[i];
}
h[pos] = tmp;
if (add + sum > ans) ans = add + sum;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++ i)
scanf("%d", &h[i]);
r[0] = r[n + 1] = inf;
for (int i = 1; i <= n; ++ i)
r[i] = min(h[i], r[i-1] + 10);
for (int i = n; i >= 1; -- i){
r[i] = min(r[i], r[i+1] + 10);
sum += r[i];
}
ans = sum;
for (int i = 1; i <= n; ++ i)
if (h[i] == r[i])
solve(i);
printf("%lld\n", ans);
return 0;
}