题目传送门
题意: 我们用一种新的纪年方式,一年有n个月,每个月有a[i]天,没有闰年。现在给出计算某一天的代价的方式为该天在该月中是第几天。我们要连续选择x天,要求这x天得到的价值最大。(这x天并不一定要在同一年内,即可以选择上一年的末尾以及本年开头这样拼接),保证x要小于一整年的天数。
思路: 假设我们已经选取了某一段连续的月份,还剩下一些,可以向前选择某一个月的月末,还可以向后选择某一个月的月初,那么肯定是选择月末更加划算,因为同样花费1天,获得的代价肯定是月末的多。所以我们知道答案一定是以某个月的某个节点开始,到某个月的月末结束,这样的一段才会价值最大。所以我们先把两年拼接起来,然后枚举右端点R,二分查找左端点L,这个即找到一个最小的L,使得sum[R]-sum[L]<=x。(这个sum计算的是天数前缀和。)我们再用一个sumb记录代价的前缀和,节省计算代价所花的时间。
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=4e5+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-6;
const double PI=acos(-1);
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;
int t=sumb[i]-sumb[p];//代价计算
t+=(a[p]+a[p]-(suma[p]-suma[i]+x)+1)*(suma[p]-suma[i]+x)/2;
//x比suma[i]-suma[p]多出的那部分的天数贡献的代价。
res=max(res,t);
}
cout<<res<<endl;
}