题目链接
题目大意:
一年中有n个月,每个月有mi天,每个月中的第i天所获得的积分为i分,要在其中找出连续的x天,使其积分和最大,这x天可以不属于同一年,问积分最多是多少?
解题思路:
首先可以知道选择的最大的那个区间一定以某个月的最后一天为终点。
证明(反证):
假设选择的最优区间为[L,R],其中R不是某个月的终点,就说明R的右边为R+1,其中L,R表示区间最左边和最右边所能获得的积分。
- L=R,此时区间右移一位,ans+(R+1)-L,所以ans增加,不是最优区间。
- L<R,此时区间右移一位,ans+(R+1)-L>ans+1,所以ans增加,不是最优区间。
- L>R。
如果L=R+1,不妨对区间一直进行右移,因为ans一直不变,直到L或R到达所在月的最后一天。如果是R先到达最后一天或者同时到达,此时积分和和假设的区间一样,可以把R是最后一天的区间当做假设区间,则此时与假设相矛盾。如果是L先到达最后一天,则再右移一次ans+(x+1)-y=ans,如果R也到达最后一天,则同样与假设矛盾,如果R没有到达最后一天,则再次右移为ans-1+R+1 > > > ans,则ans增加不是最优区间。
如果L>R+1,则对区间左移一位,ans+(L-1)-R>ans,不是最优区间。
已经知道终点必须是某个月的最后一天,所以只需要枚举每个月的最后一天作为R,然后二分寻找L所在的月份,结果就是把中间月份全都加起来,L所在月份取部分就可以了,用前缀和处理一下。
解题代码:
#include<bits/stdc++.h>
using namespace std;
mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count());
typedef long long ll;
const int maxn=(2e5+10)*2;
ll a[maxn];
ll pre[maxn],pre_sum[maxn];
//计算1+2+...+mx 的后u项的和
ll cal(ll mx,ll u)
{
ll ret=mx*(mx+1ll)/2ll;
ll sub=mx-u;
ret-=(sub+1ll)*sub/2ll;
return ret;
}
int main()
{
ll n,x;
cin>>n>>x;
for (int i=n+1;i<=n+n;i++)
{
scanf("%lld",&a[i]);
a[i-n]=a[i];
}
for(int i=1;i<=n+n;i++)
{
pre[i]=pre[i-1]+a[i];
pre_sum[i]=pre_sum[i-1]+cal(a[i],a[i]);
}
ll ans=0;
for (ll i=n+1;i<=n+n;i++)
{
ll l=1,r=i+1;
while (l+1ll<r)
{
ll mid=(l+r)>>1ll;
if (pre[i]-pre[mid-1]>=x)
l=mid;
else
r=mid;
}
ll tmp=pre_sum[i]-pre_sum[l];
ll sub=x-(pre[i]-pre[l]);
tmp+=cal(a[l],sub);
ans=max(ans,tmp);
}
cout<<ans<<endl;
return 0;
}