逃跑
jzoj 1748
题目大意
你有一个能量值l,在接下来的n天里,你每天有两个选择:
1、增加l个食物
2、使l加一,
你第i天要吃
a
i
个
a_i个
ai个食物,如果吃不到就会死掉,现在问你n天后你能活下去吗,如果能那最多剩多少个食物?(有t组数据)
输入样例
1
5 2
1 1 1 4 2
输出样例
2
样例解释
一个可行的最优方案如下:
第一天制造2个单位的食物,第二天把l升级到3,后面三天各制造3个单位的食物。
最后得到2+3+3+3-1-1-1-4-2=2单位的食物。
数据范围
对于30%的数据,
1
⩽
n
⩽
20
,
1\leqslant n\leqslant 20,
1⩽n⩽20,且测试点中只有一组数据;
另外40%的数据,
1
⩽
n
⩽
1000
。
1\leqslant n\leqslant 1000。
1⩽n⩽1000。
对于100%的数据,
1
⩽
n
⩽
100000
,
0
⩽
L
,
A
i
⩽
1
0
9
。
1\leqslant n\leqslant 100000,0\leqslant L,Ai\leqslant 10^9。
1⩽n⩽100000,0⩽L,Ai⩽109。
解题思路
我们可以如果当前是第i天,增加l的奉献是
n
−
i
n-i
n−i,即接下来
n
−
i
n-i
n−i天能量多1
当l大于
n
−
l
n-l
n−l时我们就不考虑加l了
那如果当前天不够食物,我们就要撤回之前的加l改为加食物,因为加l的奉献到当前状态还不一定会大于加食物的奉献(详情见代码)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll t, n, l, x, add, sum, a[100500];//a存某一次加l的时间
int main()
{
scanf("%lld", &t);
while(t--)
{
sum = 0;//sum就是还剩多少食物
add = 0;//add就是加了多少次l
scanf("%lld %lld", &n, &l);
for (int i = 1; i <= n && sum != -1; ++i)
{
scanf("%lld", &x);
if (sum < x)//食物不够
{
sum += add + l;//当前天造食物
while(sum < x && add > 0 && (add + l - 1) > i - a[add])//不够且还能再撤回,以及撤回这个加的是否大于对后面造成的负影响,-1是因为add少了1
{
sum = sum + add + l - 1 - (i - a[add]);//奉献以及负影响
add--;
}
if (sum < x) sum = -1;//还不够
else sum -= x;//够了
}
else
{
if (n - i > add + l) a[++add] = i;//有奉献
else sum += add + l;//无奉献
sum -= x;//减所需食物
}
}
printf("%lld\n", sum);
}
return 0;
}