CSU-2221 假装是区间众数(ST表模版题)

题目链接

题目

Description

给定一个非递减数列Ai,你只需要支持一个操作:求一段区间内出现最多的数字的出现次数。

Input

第一行两个整数N,Q

接下来一行有N个整数,表示这个序列。

接下来Q行每行一个操作:A B,询问A到B之间出现最多的数字。

1<=N,Q<=100000。-100000<=Ai<=100000

Output

每组数据若干行,每行对应一个询问的答案。

Sample Input

10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10

Sample Output

1
4
3

题解

这个题是ST表+RMQ模版题

首先题中所给序列是非递减序列,所有相等的数都会聚在一块,所以我们可以将相等的数字划为一段,\(value[i]和cnt[i]\)分别表示第i段对应的数值和出现的次数,\(num[p],L[p],R[p]\)分别表示位置p所在段的编号和左右端点的位置,每次查询(l, r)的结果分为三部分的最大值,从\(l到l\)所在段的右端点的元素个数(\(R[l]-l+1\)),从r所在段的左端点到r的元素个数(\(r-L[r]+1\)),中间第\(num[l]+1段到num[r]-1\)段cnt的最大值,这样就可以用rmq解决了。

顺便复习一下ST+RMQ

时间复杂度:预处理\(o(nlogn)\),查询\(o(1)\)

\(st[i][j]\)表示从i开始,长度为\(2^j\)的一段元素中的最小(大)值,则可以用递推的方式计算\(st[i][j]\)

\[st[i][j]=min(st[i][j-1],st[i+2^{j-1}][j-1]) \]

循环的时候注意先枚举区间长度,即先枚举j,再枚举起点i,从小区间到大区间。

查询也很简单,查询(L,R)区间内的最小(大)值,直接令x为满足\(2^x \le R-L+1\)的最大整数,则以L开头,R结尾的两个长度为\(2^x\)的区间覆盖了(L,R),由于是取最小值,所以有些元素考虑多遍也没关系,故ST表不能用于区间和

AC代码

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;

int a[maxn];
int value[maxn], cnt[maxn], num[maxn], L[maxn], R[maxn];
int st[maxn][20], lg2[maxn];
int n, q;
int k;

void ST() {
    for (int i = 1; i <= k; i++) {
        st[i][0] = cnt[i];
    }
    for (int j = 1; (1 << j) <= k; j++) {
        for (int i = 1; (i + (1 << j) - 1) <= k; i++) {
            st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
        }
    }
    for (int i = 2; i <= k; i++) {
        lg2[i] = lg2[i >> 1] + 1;
    }
}

int rmq(int l, int r) {
    if (l>r)
        return 0;
    else {
        int x = lg2[r - l + 1];
        return max(st[l][x], st[r - (1 << x) + 1][x]);
    }
}

int main() {
    scanf("%d%d", &n, &q);
    k = 0;
    value[0] = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        if (a[i] != value[k]) {
            R[k] = i;
            value[++k] = a[i];
            L[k] = i - 1;
            cnt[k] = 1;
            num[i] = k;
        }
        else {
            cnt[k]++;
            num[i] = num[i - 1];
        }
    }
    R[k] = n + 1;
    ST();
    while (q--) {
        int l, r;
        scanf("%d%d", &l, &r);
        if (num[l] == num[r]) {
            printf("%d\n", r - l + 1);
        }
        else {
            int tmp = max(R[num[l]] - l, r - L[num[r]]);
            int ans = max(tmp, rmq(num[l] + 1, num[r] - 1));
            printf("%d\n", ans);
        }
    }
    return 0;
}

/**********************************************************************
    Problem: 2221
    User: Artoriax
    Language: C++
    Result: AC
    Time:128 ms
    Memory:12572 kb
**********************************************************************/
posted @ 2019-02-13 16:38  Artoriax  阅读(278)  评论(0编辑  收藏  举报