BZOJ 2086: [Poi2010]Blocks

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2086: [Poi2010]Blocks相关的知识,希望对你有一定的参考价值。

时空隧道

 

2086: [Poi2010]Blocks

Time Limit: 20 Sec  Memory Limit: 259 MB
Submit: 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




Sample Output

5 5 2 1 1 0

HINT

 

Source

 
[Submit][Status][Discuss]

分析:

首先我们可以对于每一个元素减去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;
}
View Code

by NeighThorn














以上是关于BZOJ 2086: [Poi2010]Blocks的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 2086 [Poi2010]Blocks 单调栈

BZOJ 2086: [Poi2010]Blocks

[bzoj2086][Poi2010]Blocks_单调栈_双指针

bzoj2079[Poi2010]Guilds*

BZOJ 2095: [Poi2010]Bridges

BZOJ 2095 Poi2010 Bridges