14年的EC(银牌题),但是现在的大牛们进步神速,估计如今已经是道铜牌题了,具体我们先讲一下题意。
一个长度为N的自环圈,每个点(1~N)上有自己对应的权值(可能为负数),我们用一个初始值进入这个环,每次走到一个节点的时候会加上这个节点的权值,如果此时的权值<0,就为0了,问P步之内是否存在一点使得权值>=G。问这样的投入值的最小值。
思路:
既然我们要求的是这样的答案最小值,我们不妨可以二分答案试试,我们枚举这样的答案,放进这个环里去,要是出现了权值>G的就直接返回true就是了,然后,怎么去处理P可能为1e18这么大的一个值?我们得去寻它的周期,既然是环,就有自己的周期,可能为负,可能为0,也可能为正,这就是我们需要判断的东西了。
一个周期下来,我们可能没发比较,因为举个例子{ N=3; a[] = {-50, -90, 100}; }这样的环,一个周期下来,上升了100!可是这不是真实值,当下个周期来临的时候,会发现对应100的那个点还是100,其余点还是0,那岂不是没有改变!所以,周期不应当如此来算,我们得跑两个环来算周期差。
算出周期的差值之后,会发现周期差值会“<=0”或者“>0”,前者的话,跑两圈之后,与跑两圈以内就没有任何差别了,所以都不需要再去判断了;后者,那么如果我们的步数P足够大的话,可以跑完两圈再继续跑的话,我们就可以以第二圈为基础,往后继续跑相应的路程了,但是后者就又要相关的处理了,我们跑到最后几圈的时候就得开始慎重了,当最后一个完整圈的时候,我们得先去这个完整圈子里看看有没有符合条件的最大值,然后再去看下剩余的不足一个完整圈的那几步中的最大值,于是只需要判断最大值是否大于等于G即可了。
大纲:
- 先跑两个周期,找周期差值;
- 判断周期差值的范围「<=0 or >0」以及P与N的关系,是否是两圈之内就可以走完;
- 如果周期差值>0以及p>=2*N,就说明,可以走两圈以上,并且最大值可能会出现在后面的节点上,于是就要跑最后一个完整周期以及跑完完整周期后的剩余几步中找寻最大值。
具体的步骤就这么多,但是处理的方式可就考验思维了,一开始写了一大长串,还WA、T,然后就决定把它们放进两个函数里去处理,一下子方便了很多(虽然时间复杂度高了)。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7;
ll N, G, P, a[maxN] = {0}, b[maxN] = {0};
ll lound(ll key)
{
ll pre = key;
for(int i=1; i<=N; i++)
{
pre += a[i];
if(pre < 0) pre = 0;
}
return pre;
}
ll lound_Maxx(ll pos, ll key)
{
ll pre = key, ans = key;
for(int i=1; i<=pos; i++)
{
pre += a[i];
if(pre < 0) pre = 0;
ans = max(ans, pre);
}
return ans;
}
bool solve(ll num)
{
ll ans = num;
ll lound_1 = lound(num);
ll lound_2 = lound(lound_1);
if(lound_1 >= lound_2 || P < 2*N)
{
if(P > N)
{
if(P < 2*N) ans = max(ans, lound_Maxx(P - N, lound_1));
else ans = max(ans, lound_Maxx(N, lound_1));
}
else ans = max(ans, lound_Maxx(P, num));
return ans >= G;
}
ll tims = P/N, rest = P%N, sum_of = lound_2 - lound_1;
if((tims - 1) > (G - lound_1)/sum_of) return true;
ll ans_1 = 0, ans_2 = 0;
if(tims > 1) ans_1 = (tims - 1)*(lound_2 - lound_1) + lound_1;
else ans_1 = lound_1;
ans_1 = lound_Maxx(rest, ans_1);
if(tims > 2) ans_2 = (tims - 2)*(lound_2 - lound_1) + lound_1;
else ans_2 = lound_1;
ans_2 = lound_Maxx(N, ans_2);
ans = max(ans, max(ans_1, ans_2));
return ans >= G;
}
int main()
{
int T; scanf("%d", &T);
for(int Cas=1; Cas<=T; Cas++)
{
scanf("%lld%lld%lld", &N, &G, &P);
for(int i=1; i<=N; i++) scanf("%lld", &a[i]);
ll L = 0, R = G, mid = 0, ans = G;
while(L <= R)
{
mid = (L + R)>>1;
if(solve(mid))
{
R = mid - 1;
ans = mid;
}
else L = mid + 1;
}
printf("Case #%d: %lld\n", Cas, ans);
}
return 0;
}