http://poj.org/problem?id=3368

给出一个升序数组和 q 个查询。对每个查询,返回 a b 之间出现次数最多的那个元素的出现次数。

这一类区间查询的问题很容易想到用线段树来做。显然我们首先要线段树的节点中维护么i各区间的最大次数。但这样是不够的,如果一个查询区间跨越了两个区间,那查询区间的最大次数怎么取呢?取较大值?加和?显然不是那么简单。

仔细观察题目条件,数组本身是升序的。这表示,两个区间连接之后,最大次数改变只可能发生在两个区间交接的地方。即左区间的右端和右区间的左端一样,连接起来形成了一个比原来两区间的最大值更大的一个值。

而进行这个连接处的判断和计算,我们只需要知道左子树的右端和它在左子树中出现的次数,右子树的左端和它在右子树中出现的次数。所以,线段树的每个节点需要维护五个值:本区间内最大次数,左端点及其次数,右端点及其次数。

这里有两个细节需要注意:

1.建树时,如果合并区间左端点和左子树的左端点是相同的,但次数却不一定相同。最简单的比如两个值相同的叶子节点合并,合并区间的左端点次数为2。右端点也同样存在这个问题。只要想到了这一点,解决起来并不麻烦。只有左子树所有元素完全相同,和右子树的左端点也相同时,才会出现区间左端点次数不等于左子树左端点的情况。又由于原数组是有序,所以只要左子树的左端点等于右子树的左端点,那它们中间的值就全都相等。对于右子树情况完全相同。

2.查询时,要注意处理好跨区间的查询

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring> using namespace std; int n, q;
const int maxn = + ;
const int m4 = maxn * ;
int A[maxn]; // 线段树的每个节点维护五个值
// 左端的数,左端数在本区间内的个数
// 右端的数,右端数在本区间内的个数
// 本区间内的最大出现次数
int L[m4], Lc[m4], R[m4], Rc[m4], S[m4]; //构造线段树
void Build(int p, int l, int r)
{
// 构造叶子节点
if(l == r)
{
L[p] = R[p] = A[l];
Lc[p] = Rc[p] = ;
S[p] = ; return;
} int mid = (l + r) >> ; Build(p<<, l, mid);
Build(p<<|, mid+, r); // 更新 S[p]
int temp = ;
if(R[p<<] == L[p<<|]) temp = Rc[p<<] + Lc[p<<|];
temp = max(temp, S[p<<]);
S[p] = max(temp, S[p<<|]); // 更新 L[p] 和 R[p]
L[p] = L[p<<], R[p] = R[p<<|]; // 更新 Lc[p] 和 Rc[p] // 如果左儿子的左端点和右儿子的左端点相等
// 则本区间的左端点次数等于它俩加和
// 右端点也一样
if(L[p<<] == L[p<<|]) Lc[p] = Lc[p<<] + Lc[p<<|];
else Lc[p] = Lc[p<<];
if(R[p<<] == R[p<<|]) Rc[p] = Rc[p<<] + Rc[p<<|];
else Rc[p] = Rc[p<<|];
} //线段树查询
int Query(int p, int l, int r, int a, int b)
{
// 被查询区间整个包含,直接返回最大次数
if(l >= a && r <= b) return S[p]; int mid = (l + r) >> ;
// 左/右儿子的最大次数
int x = , y = ; if(a <= mid) x = Query(p<<, l, mid, a, b);
if(b > mid) y = Query(p<<|, mid+, r, a, b); // 第一次更新res
int res = max(x, y); // 如果左右儿子都出现在查询区间内
// 且左儿子和右儿子可以连接
// 需要考察左右儿子连接处是否出现了更大的值
if(x > && y > && R[p<<] == L[p<<|])
{
// 连接左儿子
// 一定要注意在查询区间范围内求解
// 所以用到了min
int temp = min(Rc[p<<], mid-a+);
//连接右儿子
temp += min(Lc[p<<|], b-mid); // 第二次更新res
res = max(temp, res);
} // 返回res
return res;
} int main()
{
while(scanf("%d", &n), n)
{
scanf("%d", &q); for(int i= ; i<= n; i++)
scanf("%d", A+i); Build(, , n); while(q--)
{
int a, b;
scanf("%d %d", &a, &b); printf("%d\n", Query(, , n, a, b));
}
} return ;
}

POJ3368的更多相关文章

  1. poj3368(RMQ——ST)

    Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16543   Accepted: 5985 ...

  2. POJ3368 Frequent values(RMQ线段树)

    题目大概说给一个递增序列,询问区间出现最多的数. 用莫队算法比较直观,虽然应该会T..好像也可以主席树..不过题目给的序列是有序的,因而相同的数会聚在一起. 考虑把序列分成一段一段,使每段都包含极大的 ...

  3. POJ3368(RMQ)

    Frequent values Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreas ...

  4. poj3368 uva11235 Frequent values

    Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In ad ...

  5. poj3368 Frequent values(线段树)

    Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In ad ...

  6. [poj3368]Frequent values(rmq)

    题意:给出n个数和Q个询问(l,r),对于每个询问求出(l,r)之间连续出现次数最多的次数. 解题关键:统计次数,转化为RMQ问题,运用st表求解,注意边界. 预处理复杂度:$O(n\log n)$ ...

  7. poj3368 Frequent values

    思路: 转化为RMQ. 实现: #include <cstdio> #include <cstring> #include <algorithm> using na ...

  8. POJ3368(Frequent values)--线段树

    题目在这里 3368 Accepted 7312K 1829MS C++ 6936B 题意为给你一组数据,再给定一组区间,问你这个区间内出现次数最多的元素的次数是多少. 我还记得这题是学校校赛基础的题 ...

  9. POJ3368题解

    题目大意:一个非降序序列,有若干查询,每次查询一个区间中重复次数最多的数字的个数. 思路:因为是非降序的,所以可以从头遍历把每个相同的数字划为一个块,用p[i]表示ai划分到了哪个块里面,同时还可以记 ...

随机推荐

  1. dubbo-Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError

    dubbo-2.8.4需用jdk版本为1.8,dubbo-2.5.3可以使用1.7版本的jdk.

  2. java判断一个字符串中是否包含全角

    public static boolean isAngle(String str){ if(str.getBytes().length==str.length()){ //全是半角 return tr ...

  3. 记录一次从txt文件导入数据的python下的MySQL实现

    环境: python2.7 ComsenzXP自带MySQL 安装python-MySQL模块 数据格式:txt格式的账号信息. 数据一行一条数据. 难点:有的行只有账号,没有密码:有的为空行:有的行 ...

  4. 第4章 ZK基本特性与基于Linux的ZK客户端命令行学习 4-2 session的基本原理与create命令的使用

    客户端与服务端之间存在的连接,那么这样的一个连接我们就称之为会话,也就是session.其实就相当于是我们在做JSP或者说是Service的时候,那么服务端是Servlet,客户端使用的是浏览器.浏览 ...

  5. day18-事务与连接池 5.关于不考虑事务隔离性出现的问题

  6. 【总结整理】WebGIS学习-thinkGIS(三):关于影像金字塔、瓦片行列号、分辨率resolution

    http://www.thinkgis.cn/topic/541a5206da8db186fd0673ba 1.前言 在上一节中我们知道了屏幕上一像素等于实际中多少单位长度(米或经纬度)的换算方法,而 ...

  7. javax.swing.Timer

    javax.swing 类 Timer java.lang.Object javax.swing.Timer 所有已实现的接口: Serializable public class Timerexte ...

  8. Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)

    Linux内核源码分析-链表代码分析 分析人:余旭 分析时间:2005年11月17日星期四 11:40:10 AM 雨 温度:10-11度 编号:1-4 类别:准备工作 Email:yuxu97101 ...

  9. Android的性能优化

    ArrayList和Vector ArrayList和Vector都是内部以数组实现的List,它们两唯一的区别就是对多线程的支持,ArrayList是线程不安全的,而Vector内部对大多数方法都做 ...

  10. 小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历

    在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.但在那篇文章中,只 ...