1004: Zeratul的完美区间(浅谈sparse table)

1004: Zeratul的完美区间
Time Limit:5000/3000 MS (Java/Others)   Memory Limit:294912/262144 KB (Java/Others)
Total Submissions:93   Accepted:17
[ Submit][ Status][ Discuss]

Description

如果一个长度为k区间中的数排序后正好可以形成n+1,n+2,,n+k的形式,那么我们称这个区间叫完美区间。

现在Zeratul给出了一个1n的一个全排列,并给出m[a,b],询问区间[a,b]是否是完美区间。其中a,b表示区间的起止下标。(ab)

比如16的一个全排列{142365}中,[1,4][2,6]都是完美区间,[2,5]不是完美区间。

Input

输入仅一组数据:

第一行包括一个整数n(1n100000)

第二行包括n个整数a1,a2,,an. (1ain, 对任意ij,都有aiaj)

第三行包括一个整数m(1m100000)

接下来m行,每行包括两个整数a,b(1abn),表示一组询问。

Output

对于每组询问[a,b],如果[a,b]是完美区间,输出YES,否则输出NO。

Sample Input

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

Sample Output

YES
YES
NO
点击跳转到原题
ST表简述:
    ST表即sparse table,也叫稀疏表。其特点是在海量数据中可以在O(1)的复杂度下求出任意区间的最值,前提是进行数据预处理,复杂度为O(nlogn)。
      预处理:主要是用到了二分及动态规划的思想。以最大值为例,最小值类似。建立一个动态规划数组dp[i][j],表示区间i->i+2^j-1的最大值,这样就可以表示所有区间的最值了,其中定义dp[i][0]表示元素本身。推出动态规划方程为:dp[i][j] = max(dp[i][j-1],dp[i+2^j][j-1]),思维敏捷的读者就可以发现,这里其实就是将2^j-1长的区间二分为两个2^(j-1)区间。这样就可以由动态规划方程完成初始化,复杂度显然为O(nlogn)。
      查询:从预处理的思想得出查询的方法,对于任意的区间[a,b],int k = log2(b-a+1),这样将区间划分为[a,a+2^k-1],[b-2^k+1,b],聪明的读者一定会发现,这样区间会有覆盖的部分。事实的确如此,但是我们需要的求解最值,所以对我们求解没有影响。这样就可以得到:[a,b]最大值为max(dp[a][k], dp[b-2^k+1][k])。
      小结:ST表是一种对数据处理的极好方式,通过对数据进行预处理,使得查询任意区间最值变为O(1),启发我们对于一些数据询问问题中,对数据进行合理的预处理有时候是很有效的。
分析:
    不难想到,对于任意的给定区间[a,b],只需要去验证区间内的最大值与最小值之差与区间长度是否相等即可(由于任意的元素都不相等),估算复杂度:一般的求解最值方法复杂度为O(n),共有n组查询,总复杂度为O(n^2)=1e10,可见一般的方法不能解决,所以想到采用ST表,总的复杂度为O(nlongn)+O(n)=O(nlongn)<2*1e6,可以AC。

下面是AC代码:
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int Max = 1e5+5;
int dp_max[Max][33];
int dp_min[Max][33];
int main( )
{
    //freopen("input.txt","r",stdin);
    int n;
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &dp_max[i][0]);
        dp_min[i][0] = dp_max[i][0];
    }
    //初始化(注意按照列优先的规则进行初始化),复杂度O(nlogn)
    int k = (int)log2(n);
    for(int j=1; j<=k; j++)
        for(int i=1; i<=n; i++)
            if(i+(1<<(j-1)) <= n)//注意i+2^(j-1)可能大于n
            {
                dp_max[i][j] = max(dp_max[i][j-1], dp_max[i+(1<<(j-1))][j-1]);
                dp_min[i][j] = min(dp_min[i][j-1], dp_min[i+(1<<(j-1))][j-1]);
            }
    int m;
    scanf("%d", &m);
    while(m--)
    {
        int a,b;
        scanf("%d%d", &a,&b);
        //通过稀疏表进行查询任意区间的最值,复杂度O(1)
        int length = (int)log2(b-a+1);
        int high = max(dp_max[a][length], dp_max[b-(1<<length)+1][length]);
        int low = min(dp_min[a][length], dp_min[b-(1<<length)+1][length]);
        if(high-low == b-a)
            printf("YES\n");
        else
            printf("NO\n");
    }
   return 0;
}
文中如有错误,还请各位读者及时纠正,在下不胜感激,
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值