bzoj 4385 poi2015

唔。挺简单的一道题但是很重要。。也花了很多时间。。

题目:
给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。
请找到最长的一段连续区间,使得该区间内所有数字之和不超过p

分析:
看道题的时候有点想法。
想了想感觉像是单调队列。就这么顺着往下走。。
发现肯定能选d个数该最好哈,所以统计每个点起始的可以修改的值是多少也就是f[i]=sum[i+d-1]-sum[i-1]对吧!
所已我们关心的结果就变成了 sum[i]-sum[j-1]-f_max<=p的最大的i-j+1.
所以 我们枚举右端点,维护一个下标对应的f单调递减的序列
O(2n)处理。

纠结如此之久是因为,不会写 单调队列了。好怕qaq。伦家不想afo。

但是交题的AC率啊!!!

这里写图片描述

因为WA 还写了对拍。最大的数据硬是没拍出来错。
后来果断发现自己算f时候的循环写少了。
还TLE对比下面的515221650的队短,我写的常数那么大吗?!
事实证明快读还是很有用的,毕竟一半时间呢,,,

#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
//by mars_ch
using namespace std;
int n,p,d,ans;
int sum[2000005];
int f[2000005];
int q[2000005];
int read()
{
    int s=0ll;
    char p=getchar();
    while(p<'0' ||p>'9')p=getchar();
    while(p>='0' && p<='9') s=s*10+p-'0',p=getchar();
    return s;
}
signed main()
{

    n=read(),p=read(),d=read();
    for(int i=1;i<=n;i++)
    {
        int a;
        a=read();
        sum[i]=sum[i-1]+a;
    }
    for(int i=1;i<=n-d+1;i++)
    {
        f[i]=sum[i+d-1]-sum[i-1];
    }
    int h=1,t=1,k=1;
    for(int i=d;i<=n;i++)    //枚举右端点 
    {
        while(h<=t && f[i-d+1]>=f[q[t]])
        {
            t--;
        }
        q[++t]=i-d+1;
        while(sum[i]-sum[k-1]-f[q[h]]>p)
        {
            k++;
            while(h<t && q[h]<k) h++; 
         }
         if(q[h]>=k) ans=max(ans,i-k+1); 
    }
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值