题目链接
题意:
我们假设每年有n个月,每个月有a[i]天,你可以连续的拜访你的朋友x天(x<n)以获得开心值,每天所能获得的开心值是该天在这个月中的第几天,求你能够获得的最大开心值是多少。
思路:
因为开心值是在一个月中顺序递增,那么我们拜访的这些天应该以一个月末结尾,所以我们将两年合并起来(可能会出现本年年末加上次年年尾的情况),枚举每个月的月末,然后二分查找前第x天的日期,最后计算出这x天内的开心值,最后与最大值比较即可,中间利用前缀和思想来减少重复计算。
知识点:
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1e6+7;
const int inf=0x3f3f3f3f;
int a[N],b[N],suma[N],sumb[N];
signed main()
{
int n,x;
cin>>n>>x;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=(1+a[i])*a[i]/2;
suma[i]=suma[i-1]+a[i];//天数前缀和
sumb[i]=sumb[i-1]+b[i];//开心值前缀和
}
for(int i=1;i<=n;i++)
{
a[i+n]=a[i];
b[i+n]=b[i];
suma[i+n]=suma[i-1+n]+a[i+n];//两年合并
sumb[i+n]=sumb[i-1+n]+b[i+n];//两年合并
}
int res=-inf;//开心值最大值
for(int i=1;i<=n*2;i++)//枚举每一个月末
{
if(suma[i]<x)
{
continue;
}
int p=lower_bound(suma+1,suma+2*n+1,suma[i]-x)-suma;//寻找前第x天所在月份的下标
int t=sumb[i]-sumb[p];//x天内完整月份的开心值
t+=(a[p]+a[p]-(suma[p]-suma[i]+x)+1)*(suma[p]-suma[i]+x)/2;//加上首月末的开心值
res=max(res,t);//与之前所计算过的开心值的最大值做比较
}
cout<<res<<endl;
return 0;
}