UVA 11235 Frequent values 线段树/RMQ

  vjudge 上题目链接:UVA 11235

*******************************************************大白书上解释************************************************************

  题目大意:给出一个非降序排列的整数数组 a1,a2,a3,...,an,你的任务是对于一系列询问 (i, j),回答 ai,ai+1,...,aj 中出现次数最多的值所出现的次数。

  输入格式:包含多组数据。每组数据第一行为两个整数 n 和 q(1 <= n, q <= 100000)。第二行包含 n 个非降序排列的整数 a1,a2,a3,...,an(-100000 <= ai <= 100000)。以下 q 行每行包含两个整数 i 和 j(1 <= i <= j <= n),输入结束标志为 n = 0。

  输出格式:对于每个查询,输出查询结果。

  分析:应注意到整个数组是非降序的,所有相等元素都会聚集到一起。这样就可以把整个数组进行游程编码(Run Length Encoding, RLE)。比如 -1,1,1,2,2,2,4 就可以编码成 (-1, 1), (1, 2), (2, 3), (4, 1),其中 (a, b) 表示有 b 个连续的 a。用 value[i] 和 count[i] 分别表示第 i 段的数值和出现次数,num[p], left[p], right[p] 分别表示位置 p 所在段的编号和左右端点位置,则在下图的情况,每次查询(L,R)的结果为以下 3 个部分的最大值:从 L 到 L 所在段的结束处的元素个数(即 right[L] - L + 1)、从 R 所在段的开始处到 R 处的元素个数(即 R - left[R] + 1)、中间第 num[L] + 1 段到第 num[R] - 1 段的 count 的最大值,如图 3-8 所示。

  

*******************************************************大白书上解释结束************************************************************

  我的理解:

  预处理过程主要就 3 个数组:seq[] 就是上述提到的 count[] 数组,记录 seq[i] 第 i 段连续整数的出现次数;pos[i] 表示原数组的第 i 个元素在 seq[] 中处于第几段;preSum[] 则是 seq 数组的前缀和,用于快速求出第 L 段和第 R 段的元素个数。这 3 个数组准备好后,接下来就是求区间最值的问题而已,线段树或者 RMQ 都可以,二者复杂度一样,时间差异可以忽略不计,只不过我更熟悉线段树,感觉 RMQ 的边界有点不容易处理而已。

  首先是线段树的代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
#define  For(i,s,t)  for(int i = (s); i < (t); ++i)
#define  root       int rt, int l, int r
#define  ll(rt)     ((rt) << 1)
#define  rr(rt)     (ll(rt) | 1)
#define  makemid    int mid = (l + r >> 1)
#define  lson       ll(rt), l, mid
#define  rson       rr(rt), mid + 1, r
const int N = 100005;

int c[N];
vector<int> seq, preSum;
int pos[N] = {0,};

int Max[N << 2];

inline void pushup(int rt) {   Max[rt] = max(Max[ll(rt)], Max[rr(rt)]);   }

void build(root)
{
    if(l == r) {
        Max[rt] = seq[l - 1];
        return;
    }
    makemid;
    build(lson);
    build(rson);
    pushup(rt);
}

int ql, qr;
int query(root)
{
    if(ql <= l && r <= qr) {
        return Max[rt];
    }
    makemid;
    int ret = 0;
    if(ql <= mid) {
        ret = max(ret, query(lson));
    }
    if(qr > mid) {
        ret = max(ret, query(rson));
    }
    return ret;
}

int main()
{
    int n,q;
    while(~scanf("%d",&n), n) {
        scanf("%d", &q);
        seq.clear();
        scanf("%d", c);
        int curValue = c[0], curNum = 1;
        For(i, 1, n) {
            scanf("%d", c + i);
            if(c[i] == curValue) {
                ++curNum;
                pos[i] = pos[i - 1];
            } else {
                seq.push_back(curNum);
                curValue = c[i];
                curNum = 1;
                pos[i] = pos[i - 1] + 1;
            }
        }
        seq.push_back(curNum);
        preSum.clear();
        preSum.push_back(seq[0]);
        int len = seq.size();
        For(i, 1, len) {
            preSum.push_back(preSum[i - 1] + seq[i]);
        }
        build(1, 1, len);
        int x,y;
        while(q--) {
            scanf("%d %d",&x,&y);
            --x;  --y;
            if(pos[x] == pos[y]) {
                printf("%d\n", y - x + 1);
                continue;
            }
            int lmax = preSum[pos[x]] - x;
            int rmax = y + 1 - preSum[pos[y] - 1];
            int res = max(lmax, rmax);
            if(pos[y] == pos[x] + 1) {
                printf("%d\n", res);
            } else {
                ql = pos[x] + 1  + 1;
                qr = pos[y] - 1  + 1;
                printf("%d\n", max(res, query(1, 1, len)));
            }
        }
    }
    return 0;
}

  然后是 RMQ 的:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
#define  For(i,s,t)  for(int i = (s); i < (t); ++i)
const int N = 100005;

int c[N];
vector<int> seq, preSum;
int pos[N] = {0,};

int d[N][18];
inline void init(int n)
{
    For(i, 0, n) {
        d[i][0] = seq[i];
    }
    for(int j = 1; (1 << j) < n; ++j) {
        for(int i = 0; i + (1 << j) - 1 < n; ++i) {
            d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
        }
    }
}

inline int rmq(int L, int R)
{
    int k = 0, len = R - L + 1;
    while((1 << (k + 1)) < len)   ++k;
    return max(d[L][k], d[R - (1 << k) + 1][k]);
}

int main()
{
    int n,q;
    while(~scanf("%d",&n), n) {
        scanf("%d", &q);
        seq.clear();
        scanf("%d", c);
        int curValue = c[0], curNum = 1;
        For(i, 1, n) {
            scanf("%d", c + i);
            if(c[i] == curValue) {
                ++curNum;
                pos[i] = pos[i - 1];
            } else {
                seq.push_back(curNum);
                curValue = c[i];
                curNum = 1;
                pos[i] = pos[i - 1] + 1;
            }
        }
        seq.push_back(curNum);
        preSum.clear();
        preSum.push_back(seq[0]);
        int len = seq.size();
        For(i, 1, len) {
            preSum.push_back(preSum[i - 1] + seq[i]);
        }
        init(len);
        int x,y;
        while(q--) {
            scanf("%d %d",&x,&y);
            --x;  --y;
            if(pos[x] == pos[y]) {
                printf("%d\n", y - x + 1);
                continue;
            }
            int lmax = preSum[pos[x]] - x;
            int rmax = y + 1 - preSum[pos[y] - 1];
            int res = max(lmax, rmax);
            if(pos[y] == pos[x] + 1) {
                printf("%d\n", res);
            } else {
                int ql = pos[x] + 1;
                int qr = pos[y] - 1;
                printf("%d\n", max(res, rmq(ql, qr)));
            }
        }
    }
    return 0;
}
时间: 2024-10-10 06:55:29

UVA 11235 Frequent values 线段树/RMQ的相关文章

uva 11235 - Frequent values(RMQ)

题目链接:uva 11235 - Frequent values 题目大意:给定一个非降序的整数数组,要求计算对于一些询问(i,j),回答ai,ai+1,-,aj中出现最多的数出现的次数. 解题思路:因为序列为非降序的,所以相同的数字肯定是靠在一起的,所以用o(n)的方法处理处每段相同数字的区间.然后对于每次询问: num[i]=num[j]:j?i+1 numi≠numj:max(RMQ(righti+1,reftj?1),max(righti?i+1,j?leftj+1)) #include

UVA 11235 Frequent values(RMQ)

Frequent values TimeLimit:3000Ms You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most f

UVA 11235 Frequent values

1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int maxn=100005; 7 8 int count[maxn]; 9 int num[maxn],ll[maxn],rr[maxn]; 10 int tot; 11 int d[maxn][50]; 12 13 int rmq (int l,int r){ 14 int

POJ3368 Frequent values 线段树

N个数为非递减顺序,给定范围l,r,求[l,r]区间内数字出现频率最高的次数. 可以用线段树来做.先说查询,我们设节点P对应的区间为[a, b],左孩子节点为p1,右孩子节点为p2,那么 P也许不等于 max(p1 , p2),原因是如果p1中频率较低的某个数与p2中出现频率较低的某个数是同一个数,并且两者出现次数加起来大于max(p1, p2),但是,题目说N个数为非递减顺序排列,所以这个可能只会发生在p1的右端和p2的左端对应的数是同一个数,因此,我们还要处理这第三个可能. 查询的问题解决,

(线段树区间合并)UVA 11235 - Frequent values

题意: 一个数列,多次查询L到R最多连续相同数字的数量. 分析: 显然区间合并.不过还就没写了,都有点忘了. 不过回忆一下,push_down还是写对了. 不过WA了,后来仔细想一想,光查询光用已经维护的答案还不够,还需要在query的时候再合并一下,才能更新出正确的答案. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #

UVA - 11235 —— Frequent values 【RMQ】

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23846 题解: 1. 游程编码(将序列转化为(value, num)的一段一段的键值对形式)后,将问题转化为几乎是一个RMQ问题,仅有一些细节要单独考虑 2. 如果查询的两个下标l, r属于同一段键值对,那么答案就是(r - l + 1):否则,答案是三个(或两个)值的最大值,详情见代码: #include <iostream> #include <cstdio&g

UVa 11235 Frequent values (RMQ &amp;&amp; 区间出现最多次的数的次数)

题意 : 给出一个长度为 n 的不降序序列,并且给出 q 个形如(L, R)的问询,问你这个区间出现的最多次的数的次数. 分析 : 很自然的想到将区间"缩小",例如1 1 2 3 3 3就可以变成2 1 3,构造出"数量数组",这个数组实际上就是已经将原来区间分了块,但是问询的区间不可能就是这些"数量数组"构成的"块",不过先来想想问询的区间可不可能包含这里面的某些"块"?很显然是有可能的,那么从这些&qu

uva 11235 Frequent values(游程编码+区间最小值查询)

游程编码的基本原理是:用一个符号值或串长代替具有相同值的连续符号(连续符号构成了一段连续的"行程".游程编码因此而得名),使符号长度少于原始数据的长度.只在各行或者各列数据的代码发生变化时,一次记录该代码及相同代码重复的个数,从而实现数据的压缩. 游程编码(Run Length Encoding , RLE) 例如:5555557777733322221111111 游程编码为:(5,6)(7,5)(3,3)(2,4)(1,7) 解题思路很好: 用value[i] count[i] 分

UVA 11983 - Weird Advertisement(线段树)

UVA 11983 - Weird Advertisement 题目链接 题意:给定几个矩形,之后,求覆盖k次以上的点 思路:先把坐标离散化掉,然后把每个矩形x2,y1加一,这样就把求点转化为求面积,然后每个矩形拆分成上下两个线段,按y排序之后,从下往上每访问一条线段(第一条除外),答案就加上目前整条线段上次数大于等于k的长度乘上这条线段和上一条线段的高度差,然后再用这条线段,根据它是矩形下面还是上面的线段去更新整条线段,维护线段大于等于k的长度这步利用线段树 代码: #include <cst