BZOJ 2086: [Poi2010]Blocks
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2086: [Poi2010]Blocks相关的知识,希望对你有一定的参考价值。
2086: [Poi2010]Blocks
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 486 Solved: 220
[Submit][Status][Discuss]
Description
给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于k。
总共给出M次询问,每次询问给出的k不同,你需要分别回答。
Input
第一行两个正整数N (N <= 1,000,000)和M (M <= 50)。
第二行N个正整数,第i个正整数表示a[i] (a[i] <= 10^9)。
第三行M个正整数,第i个正整数表示第i次询问的k (k <= 10^9)。
Output
共一行,输出M个正整数,第i个数表示第i次询问的答案。
Sample Input
5 6
1 2 1 1 5
1 2 3 4 5 6
1 2 1 1 5
1 2 3 4 5 6
Sample Output
5 5 2 1 1 0
HINT
Source
分析:
首先我们可以对于每一个元素减去k转化为求一段最长的正子序列的问题...
这个问题感觉一直都是nlgn的方法写的...就是维护一个单调递减的队列,然后对于每一个元素所能延伸到的最左边的点就在队列里二分...
然后nlgn是过不了这道题的...
我们考虑对于队列里队尾部分的元素一定是只能更新一次答案...因为我们从后向前枚举元素,然后寻找当前元素所能延伸到的最左边的点...那么对于队尾元素,它和后面的点更新答案显然比和前面的点更新答案要优...所以我们在用队列里的元素更新完ans之后就可以扔掉这个元素了...其实这就是单调栈了...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=1000000+5; int n,m,a[maxn],stk[maxn],tail,ans; long long b[maxn]; signed main(void){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int M=1,k;M<=m;M++){ scanf("%d",&k);tail=0;ans=0; for(int i=1;i<=n;i++) b[i]=a[i]-k,b[i]+=b[i-1]; for(int i=1;i<=n;i++) if(b[stk[tail]]>=b[i]) stk[++tail]=i; for(int i=n;i>=1;i--){ while(tail>0&&b[stk[tail-1]]<=b[i]) tail--; ans=max(ans,i-stk[tail]); } printf("%d%c",ans,M==m?‘\\n‘:‘ ‘); } return 0; }
by NeighThorn
以上是关于BZOJ 2086: [Poi2010]Blocks的主要内容,如果未能解决你的问题,请参考以下文章