2018ccpc网络赛hdu6444-Neko's loop 单调队列

博客围绕长度为n、间隔为m的序列展开,指出有gcd(n,m)种不同序列,长度为n/gcd(n,m)。通过枚举开头拓展两倍,用单调队列找最长连续子序列且长度不超m,同时考虑总和大于0时的特殊情况,提前减掉len并调整m值求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于长度为n,间隔为m,那么会有gcd(n,m)种不同序列,那
么长度为n/gcd(n,m),对于这些序列枚举开头拓展两倍相当
于找zuic最长连续子序列并且这个连续子序列长度不超过m(
m提前mod了的要是这len个数的和>=0的话)找的过程用单调
队列保存前m个最小,这里有个特殊的地方,比如这个
5 1000 5 1
1 1 -1 -1 1
 有可能会输出999,这是当总的len和大于0的时候我们直接
 选用了前五个,但其实可以选择第5 ,4 ,1 位置的3个1,
 加起来是3。于是提前减掉len再像之前那样做一下,再
 把m%len的地方改成m=len即可

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=1e4+100;
ll p[maxn],sum[2*maxn],last[2*maxn];
int gcd(int a, int b) { return a == 0 ? b : gcd(b % a, a); }
int main()
{
    int t,n,m,k,i,j,M,now,l,r,len,manys=1;
    ll s,cnt,Ans,ans,temp;
    scanf("%d",&t);
    while(t--)
    {
        Ans=-1e18;
        scanf("%d%lld%d%d",&n,&s,&m,&k);
        for(i=1;i<=n;i++) scanf("%lld",&p[i]);
        cnt=gcd(k,n); len=n/cnt;
        for(i=1;i<=cnt;i++)
        {
            now=i,ans=0;
            for(j=1;j<=2*len;j++,now=(now+k-1)%n+1)
            sum[j]=sum[j-1]+p[now];
            last[1]=1,l=r=1,M=m;
            if(sum[len]>=0) ans+=sum[len]*(M/len),M%=len;
            else M=min(M,len);
            for(j=1;j<=2*len;j++)
            {
                while(l<=r&&last[l]<j-M) l++;
                if(l<=r)
                    temp=ans+sum[j]-sum[last[l]],Ans=max(Ans,temp);
                while(l<=r&&sum[last[r]]>=sum[j]) r--;
                last[++r]=j;
            }
            if(m>=len)
            {
                M=m-len,ans=0;
                if(sum[len]>=0) ans+=sum[len]*(M/len);
                M=len,last[1]=1,l=r=1;
                for(j=1;j<=2*len;j++)
                {
                    while(l<=r&&last[l]<j-M) l++;
                    if(l<=r)
                        temp=ans+sum[j]-sum[last[l]],Ans=max(Ans,temp);
                    while(l<=r&&sum[last[r]]>=sum[j]) r--;
                    last[++r]=j;
                }
            }
        }
        printf("Case #%d: %lld\n",manys++,max(0ll,s-Ans));
    }
    return 0;
}
/*
对于长度为n,间隔为m,那么会有gcd(n,m)种不同序列,那
么长度为n/gcd(n,m),对于这些序列枚举开头拓展两倍相当
于找zuic最长连续子序列并且这个连续子序列长度不超过m(
m提前mod了的要是这len个数的和>=0的话)找的过程用单调
队列保存前m个最小,这里有个特殊的地方,比如这个
5 1000 5 1
1 1 -1 -1 1
 有可能会输出999,这是当总的len和大于0的时候我们直接
 选用了前五个,但其实可以选择第5 ,4 ,1 位置的3个1,
 加起来是3。于是提前减掉len再像之前那样做一下,再
 把m%len的地方改成m=len即可
 */

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值