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 思路分析: 典型的区间统计问题,要求求出某段区间中的极值,可以使用线段树求解. 在线段树结点中存储区间中的最小值与最大值:查询 ...
随机推荐
- 【java】实体类中 Set<对象> 按照对象的某个字段对set排序
Java利用hibernate进行一对多查询时,把另一张表作为一个属性存进这张表的字段中,返回的类型是set类型,要对返回的set类型进行排序 user表 package onlyfun.caterp ...
- 批处理文件执行cmd命令
@echo offstart "wumin" "C:\Windows\System32\cmd.exe" osk taskkill /f /im cmd.exe ...
- 使用Vue CLI3开发多页面应用
一.安装vue-cli3 1.如果你已经全局安装了旧版本的 vue-cli(1.x 或 2.x),你需要先通过 npm uninstall vue-cli -g 或 yarn global remov ...
- C#入门篇-3:数据类型及转换
using System; using System.Text; using System.Collections; using System.Collections.Generic; //003 查 ...
- 【POJ 3764】The Xor-longest Path
题目 给定一个\(n\)个点的带权无根树,求树上异或和最大的一条路径. \(n\le 10^5\) 分析 一个简单的例子 相信大家都做过这题: 给定一个\(n\)个点的带权无根树,有\(m\)个询问, ...
- [oldboy-django][2深入django]初始Form组件
http://www.cnblogs.com/wupeiqi/articles/6144178.html 1 初始Form组件 # Form验证(初始Form组件验证) - 问题: - 无法记住上次提 ...
- [python][django学习篇][4]django完成数据库代码翻译:迁移数据库(migration)
上一篇我们已经完成数据库的设计,但是仅仅是python语言,并没有真正创建了数据库表.翻译成数据库语言,真正创建数据库表由django manage.py来实现,这一过程专业术语:迁移数据库 切换到m ...
- pandas神坑:如果列有NAN,则默认给数据转换为float类型!给pandas列指定不同的数据类型。
今天碰到一个错误,一个字典取值报keyError, 一查看key, 字符串类型的数字后面多了小数点0, 变成了float的样子了. 发现了pandas一个坑:如果列有NAN,则默认给数据转换为floa ...
- [错误解决]paramiko.ssh_exception.SSHException: Error reading SSH protocol banner 设置
报错信息 上午的时候数据组的同事跟我说有几个程序报错,经过查看log发现找到报错信息: paramiko.ssh_exception.SSHException: Error reading SSH p ...
- 【转】UGUI文本字体颜色和大小等代码修改
http://blog.csdn.net/alayeshi/article/details/51842195 由于项目需求用UGUI在同个文本中显示出颜色和大小都不同的文字,我上网找个很久并没有见到相 ...