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); }}
阅读全文
0 0
- NKOJ-3768 数列操作
- NKOJ-3775 数列操作
- NKOJ 3768 数列操作(单调队列/栈+DP)
- NKOJ 3775 数列操作(单调队列+DP)
- NKOJ-3712 数列<L特供版>
- NKOJ-3777 卡牌操作
- 【例题&结论】【分治(等比数列二分求和)】NKOJ 3716 数列求和
- NKOJ 4247 老蒋的数列(乱搞)
- NKOJ 3884 (NOI 2005) 维修数列(Splay)
- 数列操作
- 数列操作
- 数列操作
- NKOJ 3798 有趣的数列 (卡特兰数+线性筛)
- NKOJ 3777 卡牌操作(线段树)
- 线段树--数列操作
- [树状数组]操作数列
- 【cogs240】数列操作【线段树】
- NKOI 1321 数列操作问题
- BZOJ 1933 [Shoi2007]Bookcase 书柜的尺寸 动态规划
- 单指标时间序列预警模型解决方案
- CentOS 6.5 下 Nginx的反向代理和负载均衡的实现
- 一些基本算法
- 大数据学习[03]:hbase安装配置
- NKOJ-3768 数列操作
- HashMap深度解析(一)
- 数据库表相关
- 有关编译原理中的4种文法
- java使用poi操作Excel
- 推荐: Mycat读写分离、主从切换学习
- spring整合JUnit单元测试
- yield关键字作用
- Ubuntu16.04下面壁纸切换软件variety设置