【Codeforces Round #645 (Div. 2) D】The Best Vacation

题目链接

【题目翻译】

给你n个月,每个月天数有d[i]天

你需要选取连续的几天(可以跨月、跨年)

然后你得到的收益为这些天是在所在月份的第几天对应的数字的和。

比如你选了第2月(设有30天)的第29、30天以及3月的第1,2天。

那么你的收益就是29+30+1+2

然后现在的问题是,让你从n个月份当中选出来连续的x天,要求他们的收益总和最大。

【题解】

首先需要明确一点,你选出来的这x天,最后一天肯定是某个月的最后一天。

不然总能有更优的方案。

证明如下:

明白这一点之后就好做了。

弄一个前i个月天数的前缀和pday[i]用来查前x天所在的月份。

再弄一个前i个月(1+...+d[1]+1+...+d[2]+1+...+d[i])的每个月天数和的前缀和。用来查前x天的收益和。

枚举最后一段在哪一个月,二分出来前x天所在月份,计算出相应代价就好。

二分的时候注意边界,然后跨年的情况,你就直接把d[]再复制一遍到后面就行。

【代码】

#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;

const int N = 4e5;

int n;
ll x,d[N+10],pday[N+10],ptot[N+10];

int main(){
    //cout<<(1LL<<62)<<endl;
    #ifdef LOCAL_DEFINE
        freopen("D:\\rush.txt","r",stdin);
    #endif
    int T;
    rei(n);rel(x);
    rep1(i,1,n) rel(d[i]);
    rep1(i,n+1,2*n) d[i] = d[i-n];
    n = n*2;
    rep1(i,1,n){
        //d[i]<=10^6
        //pday[i]<=2*10^12

        //4*10^5*10^12
        //4*10^17
        pday[i] = pday[i-1]+d[i];
        ptot[i] = ptot[i-1]+(1+d[i])*d[i]/2;
    }
    ll ans = 0;
    rep1(j,1,n){
        int l = 0,r = j,temp = j;
        //要找这样一个temp,p[j]-p[temp]<=x,p[j]-p[temp-1]>x
        while (l<=r){
            int mid = (l+r)/2;
            if ((pday[j]-pday[mid])<x){
                temp = mid;
                r = mid-1;
            }else l = mid + 1;
        }
        if (temp==0) continue;
        ll tmp = pday[j]-pday[temp];
        ll lastdays = x-tmp;
        ll totval = ptot[j]-ptot[temp];
        ll firstdays = d[temp]-lastdays;
        if (firstdays<0) continue;
        totval = (1+d[temp])*d[temp]/2 - (1+firstdays)*firstdays/2 + totval;
        ans = max(ans,totval);
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AWCXV/p/12978202.html