D. The Best Vacation(贪心+二分细节)

https://codeforces.com/problemset/problem/1358/D


思路:

二分可以想到,前缀也可以看出。可是如果对于每个点的全部包含点都去枚举,肯定不行。这里有一个贪心。考虑答案先找到一段最大的di,如果结尾不放在di的最后一天,会存在>=的情况。也就是说最后一天放每个月的最后一天不会更差。所以最后的答案一定是在结尾在每个月的最后一天。这样就变成了On枚举。

一个很细节的边界。二分的时候取l=1,这样做算sum[l-1]的时候会导致该点是默认的0,导致会减成负数从而RE

所以要不在下面出现了q<0的时候continue;不然就是把边界开到l=0,l=0的时候拿完就continue

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL sum[maxn*2];
LL d[maxn*2];
LL a[maxn*2];
LL sum1[maxn*2],sum2[maxn*2];///sum1表示数量,sum2表示代价和
bool check(LL l,LL r,LL x){
    LL res=sum1[r]-sum1[l-1];
    if(res>=x) return false;
    else return true;
}
int main(void){
   LL n,x;n=read();x=read();
   for(LL i=1;i<=n;i++) d[i]=read(),d[i+n]=d[i];
   for(LL i=1;i<=n;i++){
       sum1[i]=sum1[i-1]+d[i];
       LL temp=(1+d[i])*d[i]/2;
       sum2[i]=sum2[i-1]+temp;
   }
   for(LL i=1;i<=n;i++){
      d[i+n]=d[i];
      sum1[i+n]=sum1[i+n-1]+d[i+n];
      LL temp=(1+d[i+n])*d[i+n]/2;
      sum2[i+n]=sum2[i+n-1]+temp;
   }
   ///枚举以某个月的月底为结尾
   LL ans=0;
   for(LL i=1;i<=n*2;i++){
      LL cnt=x-d[i];
      if(cnt<=0){
        LL res=d[i]-x;
        ans=max(ans,( (1+d[i])*d[i]/2-(1+res)*res/2) );
        continue;
      }
      LL l=1;LL r=i;
      while(l<r){
        LL mid=(l+r)>>1;
        if(check(mid,i,x)) r=mid;
        else l=mid+1;
      }
      LL res=sum2[i]-sum2[l-1];
      LL num=x-(sum1[i]-sum1[l-1]);
      LL k=d[l-1];
      LL q=k-num;
      ans=max(ans,res);
      if(q<0) continue;///d[l-1]==0导致边界d[0]=0,得q<0乘起来越界re
      res+= ( (1+k)*k/2-(1+q)*q/2 );
      ans=max(ans,res);
   }
   printf("%lld\n",ans);
   return 0;
}
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+1000;
typedef long long LL;
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL sum[maxn*2];
LL d[maxn*2];
LL a[maxn*2];
LL sum1[maxn*2],sum2[maxn*2];///sum1表示数量,sum2表示代价和
bool check(LL l,LL r,LL x){
    LL res=sum1[r]-sum1[l-1];
    if(res>=x) return false;
    else return true;
}
int main(void){
   LL n,x;n=read();x=read();
   for(LL i=1;i<=n;i++) d[i]=read(),d[i+n]=d[i];
   for(LL i=1;i<=n;i++){
       sum1[i]=sum1[i-1]+d[i];
       LL temp=(1+d[i])*d[i]/2;
       sum2[i]=sum2[i-1]+temp;
   }
   for(LL i=1;i<=n;i++){
      d[i+n]=d[i];
      sum1[i+n]=sum1[i+n-1]+d[i+n];
      LL temp=(1+d[i+n])*d[i+n]/2;
      sum2[i+n]=sum2[i+n-1]+temp;
   }
   ///枚举以某个月的月底为结尾
   LL ans=0;
   for(LL i=1;i<=n*2;i++){
      LL cnt=x-d[i];
      if(cnt<=0){
        LL res=d[i]-x;
        ans=max(ans,( (1+d[i])*d[i]/2-(1+res)*res/2) );
        continue;
      }
      LL l=0;LL r=i;
      while(l<r){
        LL mid=(l+r)>>1;
        if(check(mid,i,x)) r=mid;
        else l=mid+1;
      }
      LL res=sum2[i]-sum2[l-1];
      LL num=x-(sum1[i]-sum1[l-1]);
      LL k=d[l-1];
      LL q=k-num;
      ans=max(ans,res);
      if(l==0) continue;
      ///if(q<0) continue;///d[l-1]==0导致边界d[0]=0,得q<0乘起来越界re
      res+= ( (1+k)*k/2-(1+q)*q/2 );
      ans=max(ans,res);
   }
   printf("%lld\n",ans);
   return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/115353303