NKOJ-3768 数列操作

来源:互联网 发布:淘宝黄金品级调整箱cdk 编辑:程序博客网 时间:2024/05/09 10:17

P3768 数列操作
问题描述

    给出 N 个正整数数列 a[1..N],再给出一个正整数 k,现在可以重复进行如下操作:        每次选择一个大于 k 的正整数 a[i],将 a[i]减去 1,选择 a[i-1]或 a[i+1]中的一个加上 1。    经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于 k。                                     总共给出 M 次询问,每次询问给出的 k 不同,你需要分别回答。。

输入格式

第一行两个正整数 N 和 M  。               第二行 N 个正整数,第 i 个正整数表示 a[i] 。    第三行 M 个正整数,第 i 个正整数表示第 i 次询问的 k 。   

输出格式

共一行,输出 M 个正整数,第 i 个数表示第 i 次询问的答案。   

输入输出样例
样例输入 1

5 6             1 2 1 1 5   1   2   3   4   5   6

样例输出 1

5 5 2 1 1 0

样例输入 2

6 4     3 2 1 2 5 13   4   1   6

样例输出 2

2 1 6 0

数据范围

对于 40%的数据, 1<=N<=100    1<=M<=50    对于 100%的数据,1<=N<=300000 1<=M<=50a[i] <= 10^9    k <= 10^9

你永远想不到第一题有多难

题解

先简化一下题意

题目当中的操作其实可以转化为:
对于区间[l,r],若sum[l,r]>=(r-l+1)*k,则该区间满足要求 操作后每个数字不小于k

然后就是一件很纠结的事情

其实我们算的是 sum[l,r] - (r-l+1)*k 是否大于0
但是对于不同的区间[l,r],减去的(r-l+1)*k是不同的 这就很难处理了

所以我们先做一点简单的

对于区间[1,n] 求出最长的子区间[l,r] 满足sum[l,r]>=0
这道题目就简单多了

①求出前缀和的单调递减序列
②求出满足sum[1,r]-sum[1,l]>0的 r-l 最大值

例如 区间元素为       1 -2 1 1 -4  1 2那么前缀和为          1 -1 0 1 -3 -2 0单调递减序列 0(pos=0)   -1     -3于是在求r-l的最大值时 我们就枚举pos=7 -> pos=1求出对于r 最小的单调序列的编号pos=7时 单调队列的当前编号为5,对应的前缀和为-3sum[7] - sum[5] = 0(第七个位置的前缀和)-(-3)(第五个位置的前缀和)=3>0所以可以更新最大长度为 7-5+1=3如此往复 即可得到对于7的最长序列为 7(pos=0的点sum=0->满足条件)

为什么我们只需要考虑单调队列中的元素呢?

对于单调队列中的任意两个连续的下标 l,r 当天讨论的点为 p如果 sum[p]-sum[l]<0那么由于队列的单调性 sum[p+k]>=sum[p] (p+k<l)如果sum[p]不满足要求     那么由于sum[p+k]>=sum[p]所以sum[p+k]也一定不满足要求反之 如果sum[p+k]满足要求 那么sum[p]也一定能满足要求

小问题解决了

那么我们能不能把原来的问题转化成这个问题呢?

答案是肯定的

我们可以把数列中的每一个数字都减去 k
转化后 我们求得等价于原来的
sum[l,r] -(r-l+1)k >= (r-l+1)k -(r-l+1)k
[黑色的字体即为减去的k们]

问题解决

注意

①要用long long(套路真的深)
②对于最长的满足要求的序列即为原序列的特殊情况,我们应该把队列中的第一个元素位置赋值为0 sum[0]=0 (具体原因自己想想 当然你也可以先WA再想)

附上对拍代码

#include <iostream>#include <cstdio>using namespace std;inline long long input(){    char c=getchar();long long o;    while(c>57||c<48)c=getchar();    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;    return o;}long long n,m,res,k,top;long long a[300123],sum[300123],sq[300123];int main(){//  freopen("sequence.in","r",stdin);//  freopen("sequence.out","w",stdout);    n=input();m=input();    for(long long i=1;i<=n;i++)a[i]=input();    for(long long t=1;t<=m;t++)    {        k=input();res=0;top=1;        for(long long i=1;i<=n;i++)        {            sum[i]=sum[i-1]+a[i]-k;            if(sum[i]<sum[sq[top]])sq[++top]=i;        }        for(long long i=n;i;i--)        {            while(top&&sum[sq[top]]<=sum[i])top--;            res=max(res,i-sq[top+1]);        }        printf("%d ",res);    }}