2018CCPC网络赛:Neko's loop(单调队列)

版权声明:谁他喵没事儿会转我的博客啊QAQ,有没有人看都是一回事儿。。。 https://blog.csdn.net/f2935552941/article/details/82084231

题目大意:

给你n个数,每次可以选择从任意一点开始跳跃,每次跳跃至 (i+k)%n 的位置并获得当前位置的最大快乐值,求跳m步的过程中能够获得的最大快乐值,可以在任何时刻终止该操作。

解题思路:

其实这个题初看比较复杂,但是无聊打了个表发现对于 长度为n 的序列,它所有的循环节是等长的。等于说整个序列就是由一些循环节构成的,那么我们把循环节专门挑出来计算跳m步即可。

那么问题就转变为了给你一个序列,求长度小于m的连续序列的最大和。首先可以确定是用单调队列来求解的。但是这里需要注意一下,因为我们求的是小于m的连续序列的最大和,m是有可能大于n的,所以我们需要将原先的序列扩展为3倍,之后 将我们需要计算的长度设置为 m%n+n 计算,最后再将多余的整个序列的和加上即可,具体实现和细节看代码= = 

Ac代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
const ll INF=1e18+9;
int n,m,k,cnt,a[maxn];
ll s,sum[maxn],que[maxn];
vector<int> v[maxn];    //储存循环节
bool vis[maxn];
ll slove(int tot)
{
    int gk=v[tot].size(),cnt=0;
    for(int i=0;i<v[tot].size();i++) cnt++,sum[cnt]=sum[cnt-1]+v[tot][i];   //将序列扩展3倍
    for(int i=0;i<v[tot].size();i++) cnt++,sum[cnt]=sum[cnt-1]+v[tot][i];
    for(int i=0;i<v[tot].size();i++) cnt++,sum[cnt]=sum[cnt-1]+v[tot][i];
    int head=0,tail=0,nn=gk*3,kk=m%gk+gk;   //定义kk为m%n+n
    kk=min(kk,m);   //与m取较小值
    ll res=0;
    for(int i=1;i<=nn;i++)  //利用单调队列计算答案
    {
        if(kk==0) continue;
        while(head<tail&&sum[i-1]<sum[que[tail-1]]) tail--;
        que[tail++]=i-1;
        while(head<tail&&i-que[head]>kk) head++;
        res=max(res,sum[i]-sum[que[head]]);
    }
    res=max(res,res+(m-kk)/gk*sum[gk]); //将多余的数加上取较大值
    return res;
}
int main()
{
    int QAQ,kase=0;
    scanf("%d",&QAQ);
    while(QAQ--)
    {
        cnt=0;
        memset(vis,0,sizeof vis);
        scanf("%d%lld%d%d",&n,&s,&m,&k);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        for(int i=0;i<n;i++)
        {
            if(vis[i]) continue;
            int tmp=i;
            while(!vis[tmp])    //处理出所有的循环节
            {
                vis[tmp]=1;
                v[cnt].push_back(a[tmp]);
                tmp=(tmp+k)%n;
            }
            cnt++;
        }
        ll res=0;
        for(int i=0;i<cnt;i++) res=max(res,slove(i)),v[i].clear();  //计算每个循环节的答案取最大值
        res=max(0LL,s-res); //处理出答案
        printf("Case #%d: %lld\n",++kase,res);
    }
}

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/82084231
今日推荐