POJ 3368 Frequent values 线段树与RMQ解法
题意:给出n个数的非递减序列,进行q次查询。每次查询给出两个数a,b,求出第a个数到第b个数之间数字的最大频数。
如序列:-1 -1 1 1 1 1 2 2 3
第2个数到第5个数之间出现次数最多的是数字1,它的频数3。
思路:假设查询时的参数为a, b。这道题查询时有以下两种情况:
1、 num[a] = num[b]. 即区间内的数字全相同,此时答案为b - a + 1。
2、 如果不相同,则以一般情况来讨论。见下图。

因为序列为非递减序列,因此值相同的数字必然连续出现。将区间分为3部分。num[a]以及与它值相同的区域构成第一部分,num[b]以及与它值相同的区域构成第三部分。区间[a, b]中剩下的构成第二部分。
定义left[i]表示与num[i]值相等的数字从左起开始的下标,right[i]表示与num[i]值相等的数字从右起开始的下标。
由图易知,第二部分里的数字,left与right值均在区间[a,b]内。
当给出区间范围a,b后,第一部分在区间内出现的次数为right[a] - a + 1。第三部分在区间内出现的次数为b - left[b] + 1。
如果right[a] + 1 > left[b] - 1,说明区间没有第二部分,直接输出上面两个值中的较大者。
如果存在第二部分,需要求出第二部分里的最大频数。不过这次就非常好求了,因为所有的数开始和结束都是在第二部分中,不存在部分出现的情况。定义tmax[i] = right[i] - left[i] + 1。则第二部分里数字的最大出现次数,即为该区间内tmax的最大值。将该值求出后与前面一三部分求出的较大者比较,最大的值即为最终答案。
因为查询量巨大,当第二部分需要计算时,可以采用线段树或者rmq。
现将两种方法的代码都给出。根据提交的结果来看,线段树所需空间远小于rmq,且速度稍快一点(不排除服务器的偶然性以及我rmq代码的效率比较低等原因)。
线段树求解代码
#include<stdio.h>
#include<algorithm>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define maxn 100020
#define inf 0x3f3f3f3f
using namespace std; int num[maxn], left[maxn], right[maxn], tmax[maxn<<];
void PushUp(int rt)
{
tmax[rt] = max(tmax[rt<<], tmax[rt<<|]);
}
void build(int l,int r,int rt)
{
if (l == r)
{
tmax[rt] = right[l] - left[l] + ;
return;
}
int m = (l + r) >> ;
build(lson);
build(rson);
PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if (L <= l && r <= R) return tmax[rt];
int m = (l + r) >> ;
int ret = -inf;
if (L <= m) ret = max(ret, query(L, R, lson));
if (m < R) ret = max(ret, query(L, R, rson));
return ret;
}
int main()
{
int n, q;
//freopen("data.in", "r", stdin);
while (~scanf("%d",&n) && n)
{
scanf("%d",&q);
for (int i = ; i < n; i++)
{
scanf("%d",&num[i]);
if (!i || num[i] != num[i-]) left[i] = i;
else left[i] = left[i-];
}
for (int i = n - ; i > -; i--)
{
if (i == (n - ) ||num[i] != num[i+])
right[i] = i;
else right[i] = right[i+];
}
build(, n - , );
while (q--)
{
int a, b;
scanf("%d%d",&a,&b);
a--; b--;
if (num[b] == num[a]) printf("%d\n", b - a + );
else
{
int tem = max(right[a] - a + , b - left[b] + );
if (right[a] + > left[b] - ) printf("%d\n",tem);
else printf("%d\n", max(tem, query(right[a] + , left[b] - , , n - , )));
}
}
}
return ;
}
================================
rmq st算法求解代码
#include<stdio.h>
#include<math.h>
#include<algorithm>
#define maxn 100020
using namespace std; int num[maxn], left[maxn], right[maxn], tmax[maxn][];
void st(int n)
{
int k = (int)(log((double)n) / log(2.0));
for (int i = ; i < n; i++)
tmax[i][] = right[i] - left[i] + ;//递推的初值
for (int j = ; j <= k; j++)
for (int i = ; i + ( << j) - < n; i++)
{
int m = i + ( << (j - ));//求出中间值
tmax[i][j] = max(tmax[i][j-], tmax[m][j-]);
}
}
//查询i和j之间的最值,注意i是从0开始的
int rmq(int i, int j)
{
int k = (int)(log(double(j - i + )) / log(2.0));
int t1 = max(tmax[i][k], tmax[j-(<<k)+][k]);
return t1;
}
int main()
{
int n, q;
//freopen("data.in", "r", stdin);
while (~scanf("%d",&n) && n)
{
scanf("%d",&q);
for (int i = ; i < n; i++)
{
scanf("%d",&num[i]);
if (!i || num[i] != num[i-]) left[i] = i;
else left[i] = left[i-];
}
for (int i = n - ; i > -; i--)
{
if (i == (n - ) ||num[i] != num[i+])
right[i] = i;
else right[i] = right[i+];
}
st(n);
while (q--)
{
int a, b;
scanf("%d%d",&a,&b);
a--; b--;
if (num[b] == num[a]) printf("%d\n", b - a + );
else
{
int tem = max(right[a] - a + , b - left[b] + );
if (right[a] + > left[b] - ) printf("%d\n",tem);
else printf("%d\n", max(tem, rmq(right[a] + , left[b] - )));
}
}
}
return ;
}
POJ 3368 Frequent values 线段树与RMQ解法的更多相关文章
- POJ 3368 Frequent values 【ST表RMQ 维护区间频率最大值】
传送门:http://poj.org/problem?id=3368 Frequent values Time Limit: 2000MS Memory Limit: 65536K Total S ...
- UVA 11235 Frequent values 线段树/RMQ
vjudge 上题目链接:UVA 11235 *******************************************************大白书上解释**************** ...
- POJ 3368 Frequent values RMQ ST算法/线段树
Frequent values Time Limit: 2000MS Memory Lim ...
- [RMQ] [线段树] POJ 3368 Frequent Values
一句话,多次查询区间的众数的次数 注意多组数据!!!! RMQ方法: 预处理 i 及其之前相同的数的个数 再倒着预处理出 i 到不是与 a[i] 相等的位置之前的一个位置, 查询时分成相同的一段和不同 ...
- poj 3368 Frequent values(RMQ)
/************************************************************ 题目: Frequent values(poj 3368) 链接: http ...
- POJ 3368 Frequent values (基础RMQ)
Frequent values Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 14742 Accepted: 5354 ...
- poj 3368 Frequent values(段树)
Frequent values Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 13516 Accepted: 4971 ...
- poj 3368 Frequent values -Sparse-Table
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 16537 Accepted: 5981 Description You ...
- poj 3264 Balanced Lineup(线段树、RMQ)
题目链接: http://poj.org/problem?id=3264 思路分析: 典型的区间统计问题,要求求出某段区间中的极值,可以使用线段树求解. 在线段树结点中存储区间中的最小值与最大值:查询 ...
随机推荐
- 4 Template层 -定义模板
1.模板介绍 作为Web框架,Django提供了模板,可以很便利的动态生成HTML 模版系统致力于表达外观,而不是程序逻辑 模板的设计实现了业务逻辑(view)与显示内容(template)的分离,一 ...
- 1026: [SCOI2009]windy数(数位dp)
1026: [SCOI2009]windy数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9016 Solved: 4085[Submit][Sta ...
- linux环境搭建系列之Apache ant安装步骤
前提: linux centOS6.6 64位操作系统 ROOT账号 JDK1.7 注意:JDK和Ant版本一定要是对应的,因为我的JDK是1.7版本的,所以我在官网下载的ant是1.9的 1.把安装 ...
- 浅谈JavaScript中的函数问题
前面的话:JavaScript可运行在所有主要平台的主流浏览器上,也可运行在每一个主流操作系统的服务器端上.所以呢,要想成为一名优秀的全栈工程师,必须懂得JavaScript语言.这是我整理的JS的部 ...
- 通过sql查询rman备份信息
通过sql查询rman备份信息 查看所有备份集 SELECT A.RECID "BACKUP SET", A.SET_STAMP, DECODE (B.INCREMENTAL_LE ...
- 12c可插拔数据库CDB与PDB管理总结
12c可插拔数据库CDB与PDB管理总结 创建pdb1.直接创建 CREATE PLUGGABLE DATABASE pdb2 ADMIN USER boswll IDENTIFIED BY orac ...
- HDU 2036 求任意多边形面积向量叉乘
三角形的面积可以使用向量的叉积来求: 对于 三角形的面积 等于: [(x2 - x1)*(y3 - y1)- ( y2 - y1 ) * ( x3 - x1 ) ] / 2.0 但是面积是有方向的, ...
- [HEOI2016/TJOI2016][bzoj4554] 游戏 [建图+最大流]
题面 传送门 思路 看到棋盘摆放和棋子冲突,再加上这么小的数据范围,你能想到什么? 网络流棋盘模型啊! 就是 把源点连到每一行,每一列连到汇点,再在中间...... 等等,这道题每行不一定全部冲突?? ...
- BZOJ4555 [Tjoi2016&Heoi2016]求和 【第二类斯特林数 + NTT】
题目 在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心. 现在他想计算这样一个函数的值: S(i, j)表示第二类斯特林数,递推公式为: S(i, j) = j ∗ S(i − 1, j) + ...
- vue使用stylus
在package.json中添加 stylus-loader "css-loader": "^0.28.0", "stylus-loader&quo ...