hdu_6444_Neko's loop_(单调队列)

版权声明: https://blog.csdn.net/yyy_3y/article/details/82193073

传送门

题意:n个数字组成一个环(每个点都有一个价值a i ,从任意点开始,每次可以跳k步,一共可以跳m次,问最少需要补充多少价值,才能使价值达到s。

思路:
1)首先,显然,对于每个数字只可能在一个环中。所以我们只需要开一个数组维护一下是否访问过就行,这样就可以预处理出每个循环节。
2)对于每个循环节,我们计算出可以循环几圈,讨论一下一圈的正负,计算一下余数对最后答案的影响。
3)这样就会有一个模型:现在有n个数字,求小于等于m个数字的连续的最大字段和。
可以用单调队列维护前缀和的最小值,因为当前的sum[i]-单调队列维护的最小sum[j]能保证对于当前的i最优。

#include<bits/stdc++.h>
#define debug(a) cout << #a << " " << a << endl
#define lnn putchar('\n')
#define see putchar(' ')
#define LL long long
#define ull unsigned long long
#define PI acos(-1.0)
#define eps 1e-6
const int N=2e5+7;
using namespace std;
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
LL a[N],vis[N];
vector<LL> stk;
LL sum[N];
LL n,s,m,k;
LL q[N];
LL head,rail;
void init()
{
    head=1,rail=0;
    memset(q,0,sizeof q);
}
void pop(int i,int len)
{
    while(i-q[head]>len) head++;
}
void push(int i)
{
    while(head<=rail && sum[i]<=sum[q[rail]]) rail--;

}
LL F(int m)
{
    int len=stk.size();
    for(int i=1;i<=len;i++) sum[i]=sum[i-1]+stk[i-1];
    for(int i=1;i<=len;i++) sum[i+len]=sum[len]+sum[i];
    for(int i=1;i<=len;i++) sum[i+2*len]=sum[2*len]+sum[i];
    init();
    LL res=0;
    for(int i=1;i<=3*len;i++){
        push(i);
        q[++rail]=i;
        pop(i,m);
        if(head<=rail)
            res=max(res,sum[i]-sum[q[head]]);
    }
    return res;
}
int main ()
{
    //yyy_3y
    //freopen("1.in","r",stdin);
    int Case;
    int CASE=1;
    for(scanf("%d",&Case);Case--;){
        scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
        for(int i=0;i<n;i++) scanf("%lld",&a[i]);
        memset(vis,0,sizeof vis);
        LL ans=0;
        for(int i=0;i<n;i++){
            if(vis[i]) continue;
            stk.clear();
            int j=i;
            while(!vis[j]){
                stk.push_back(a[j]);
                vis[j]=1;
                j=(j+k)%n;
            }
            LL sum=0;
            int sz=stk.size();
            for(int i=0;i<sz;i++) sum+=stk[i];
            LL x=m%stk.size();
            LL y=m/stk.size();
            if(y>0){
                y--;
                x+=sz;
            }
            LL res=F(x);
            ans=max(ans,res);
            if(sum>0) res+=y*sum;
            ans=max(ans,res);
        }
        printf("Case #%d: %lld\n",CASE++,max(s-ans,0LL));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yyy_3y/article/details/82193073
今日推荐