【Comet OJ - 2019国庆欢乐赛 F】 高速公路

题意

一个长度为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值