Codeforces Round #645 (Div. 2)-D. The Best Vacation(思维,二分,lower_bound,upper_bound)

题目链接

题意:

我们假设每年有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;
}

猜你喜欢

转载自blog.csdn.net/ACkingdom/article/details/106410912