现在的位置: 首页 > 综合 > 正文

[CF 279C]Ladder[segment]

2018年03月17日 ⁄ 综合 ⁄ 共 1210字 ⁄ 字号 评论关闭

题意:

给出一列数,满足b1 ≤ b2 ≤ ... ≤ bx ≥ bx + 1 ≥ bx + 2... ≥ bk的子段为Ladder.
给出一系列询问, 判断该子段是否构成Ladder.

思路:

朴素地想:

对于一个询问, 向右扫描,

若一直不减, yes.

若遇到第一个减少项, 则记录其前驱, 看此后是否一直不增. 若是, yes. 否则,no.

这样的话, 最多是O(n*m)的...1e10的数量级, 超时...

注意到询问是动态的, 而数列是静态的. 可以预处理, 记录下数列的性质, 查询的时候直接调用即可. 这里就用到了dp记忆化搜索...

首先考虑需要记录的性质是什么.

判断是否是ladder需要知道的信息是什么?

该点之后不增的最远点和不减的最远点...

dp[i].u   dp[i].d

...但是这样好像并没有什么递推式...

看题解→ →

http://blog.sina.com.cn/s/blog_6ffc3bde01017jqj.html

题意:给一个数列,查询区间[l,r]内是否存在b1 ≤ b2 ≤ ... ≤ bx ≥ bx + 1 ≥ bx + 2... ≥ bk或是非递增、非递减序列。
经常可以遇到这类问题,一般解法是求每个元素的单边最值区间或双边最值区间,我最初的想法是记录从每个元素开始,
在哪里开始递增和在哪里开始递减的,然后进行一系列纠结的运算。然后我看了看大神们的想法,果然很犀利,
只需要记录每个元素左边的递减区间和右边的递增区间,然后判断R[l]和L[r]的大小就可以了。
判断有没有交叠就可以了。

嗯,确实是这样.如果按原先的想法, 总是走一步判一步, 缺乏全局性, 而这种预处理是可以获得全局性质的. 因此需要记录的是全局性质.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
int L[MAXN],R[MAXN];
//left non-increasing segment
//right non-decreasing segment
int a[MAXN],n,m;

void solve()
{
    L[0] = 0;
    for(int i=1;i<n;i++)
        if(a[i]<=a[i-1])    L[i] = L[i-1];
        else                L[i] = i;
    R[n-1] = n-1;
    for(int i=n-2;i>=0;i--)
        if(a[i]<=a[i+1])    R[i] = R[i+1];
        else                R[i] = i;
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
    }
    solve();
    for(int i=0;i<m;i++)
    {
        int l,r;
        scanf("%d %d",&l,&r);
        if(R[--l]>=L[--r])  printf("Yes\n");
        else            printf("No\n");
    }
}




抱歉!评论已关闭.