Frequent Values-线段树求解出现最多的数
Frequent Values(poj 3368)
注意:以下答案为离线作答结果,并非能通过poj,若要通过poj,需要修改函数接口,因为以下程序接受半封闭区间(s,e],同时还需要修改输入数据的顺序
求出现最多的数:
给定n个数,已按从大到小顺序排列好,一共有q个询问,每次询问一个区间,问这个区间中出现次数最多的数是什么。
题目数据范围:
数的个数,1 ≤ n ≤ 100000
询问次数,1 ≤ q ≤ 100000
每个数的大小,-100000 ≤ ai ≤ 100000
思路:
使用线段树解决。因个数比较多,最多有10w个数据,又因为输入是有序的,从大到小输入。因此可以将相同数据保存到同一个结构体中,并记录一共有多少个相同的元素。这样做既可以免去合并子树求解出现最多数的麻烦,也节约了空间,提高了效率。
题目中使用数组来模拟线段树。
/**********************************************************
作者:SunboyL
题目3:Frequent Values(poj 3368) 求出现最多的数:
给定n个数,已按从大到小顺序排列好,一共有q个询问,每次询问一个区间,问这个区间中出现次数最多的数是什么。
题目数据范围:
数的个数,1 ≤ n ≤ 100000
询问次数,1 ≤ q ≤ 100000
每个数的大小,-100000 ≤ ai ≤ 100000 思路:很容易想到建立线段树,并在线段树的每个结点中保存区间中出现次数最多的数。
需要解决的问题,两个子结点的信息如何合并到父结点。
很显然,子结点中出现次数最多的数不一定就是父结点中出现次数最多的数 时间:2013.10.28
**********************************************************/ #include <vector>
#include <algorithm>
#define MAXN (100000) typedef struct _dataSet
{// 一个值相同的数据集合
int value,count,lastNumSeq; //value表示一个值;count表示有多少个value值,seq表示在线性表中该集合最后一个值的序号
int setSeq;// 集合序号,表示第几个集合
}DataSet; typedef struct _segTree
{
int s,e; // 表示区段[s,e)
DataSet mostValue; // mostValue表示区段[s,e)中出现最多的值.
}SegTree,*pSegTree; void buildSegTree(pSegTree tree,int start,int end,int node)
{ // tree为树根节点,start到end区间为[start,end),node表示节点编号
tree[node].s = start;
tree[node].e = end;
tree[node].mostValue.value = 0x80000000;//0x80000000为最小的32位整数
if(start + == end)
return;
int mid = (start + end) >> ;
buildSegTree(tree,start,mid,node*); // 建立左子树
buildSegTree(tree,mid,end,node*+); // 建立右子树
} void insertDataSet(pSegTree segTree,const DataSet& dataSet,int node = )
{// 将dataSet插入到segTree中,node默认指向根节点,调用时忽略此参数
if(dataSet.count > segTree[node].mostValue.count)
segTree[node].mostValue = dataSet;// 每个节点都记录区间中出现次数最多的数
if(segTree[node].s + == segTree[node].e)
return;// 叶子节点
int mid = (segTree[node].s + segTree[node].e) / ;
if(dataSet.setSeq < mid) // 线段树以集合的个数来划分线段,因此使用集合所在的序号判断插入到左子树或是右子树
insertDataSet(segTree,dataSet,node*);
else
insertDataSet(segTree,dataSet,node*+);
} DataSet query(pSegTree tree,const std::vector<DataSet>& dataSet,int l,int r,int node = )
{// 查询在线性列表dataSet[]中(l,r]区间出现次数最多的数,并返回该数的相同元素集合
DataSet result;
int mid = (tree[node].s + tree[node].e) / ;
int ds,de; // 将以集合为单位的区间转换为以单个数据值为单位的区间[ds,de)
if(tree[node].s == )
ds = ;
else
ds = dataSet[tree[node].s - ].lastNumSeq + ;
de = dataSet[tree[node].e - ].lastNumSeq + ; // 因为std::vector<DataSet>& dataSet数组从0开始,所以相对起始位置,应该是减2 if(l == ds && r == de)
{
return tree[node].mostValue;
}
if(tree[node].s + == tree[node].e)
{
result = tree[node].mostValue;
result.count = r - l;// 区间并不完全覆盖相同元素的集合,则相同的个数为其部分覆盖的个数。
return result;
}
DataSet a,b;
a.count = b.count = ;
if(l <= dataSet[mid - ].lastNumSeq)
a = query(tree,dataSet,l,std::min(r,dataSet[mid - ].lastNumSeq + ),node*);
if(r > dataSet[mid - ].lastNumSeq + )
b = query(tree,dataSet,std::max(l,dataSet[mid - ].lastNumSeq + ),r,node*+);
result = a.count>b.count?a:b;
return result;
} void frequentValues()
{// frequentValues函数是本程序接口。负责输入。
int n,i; // n:有多少个要输入的元素
std::vector<DataSet> dataSet; // 本题目主要研究线段树而非线性表,这里就使用一下容器偷懒
DataSet temp; std::cin >> n; // 输入一共有多少个数据
if(n < || n > MAXN) // 元素个数为1到100000
return; std::cin >> temp.value;// 输入第一个元素的值
temp.count = ;
temp.lastNumSeq = ;
temp.setSeq = ;// 第一个集合
dataSet.push_back(temp);
int q = ; // 指向dataSet数组中当前值(集合)的指针 for( i = ;i <= n;++i )
{
std::cin >> temp.value;
if(temp.value == dataSet[q].value)
{
dataSet[q].count ++;
dataSet[q].lastNumSeq ++;
}
else
{
temp.count = ; // 值不相同,表示另外一个集合,充值value值有一个,序号为前一个集合的最后一个值的序号加一
temp.lastNumSeq = dataSet[q].lastNumSeq + ;
temp.setSeq = dataSet[q++].setSeq + ;
dataSet.push_back(temp);
}
}
int size = dataSet.size(); // 记录值不同的集合有多少个,并利用这个,建立1-size的线段树
pSegTree segTree = new SegTree[*size]; buildSegTree(segTree,,size+,); // 以segTree为根节点,集合个数size(1-size+1)为线段区间建立线段树
for(i = ;i < size;++i)
insertDataSet(segTree,dataSet[i]);// 将dataSet中的数据集合插入到线段树中 int ask; // 多少次询问
std::cin >> ask;
while(ask --)
{
DataSet result;
int l,r;
std::cin >> l >> r;// 输入要询问的区间[l,r)
result = query(segTree,dataSet,l,r);
int t = result.value;
std::cout << t << std::endl;
}
delete[] segTree; } #include <iostream>
#include <list>
using namespace std;
int main()
{
//MergeSortTest(); // 测试题目一,归并排序
//movableWindowsTest();// 测试题目二,移动窗口,输出窗口中的最小值
//MonkeyKingTest();// 测试题目三,猴王monkey king
frequentValues(); // 测试题目四,Frequently Values return ;
}
20
50 48 48 48 48 47 39 39 39 39 20 20 17 16 16 16 16 16 12 11
5
1 21
3 12
5 9
7 17
7 18
输出结果如下:

Frequent Values-线段树求解出现最多的数的更多相关文章
- POJ 3368 Frequent values 线段树与RMQ解法
题意:给出n个数的非递减序列,进行q次查询.每次查询给出两个数a,b,求出第a个数到第b个数之间数字的最大频数. 如序列:-1 -1 1 1 1 1 2 2 3 第2个数到第5个数之间出现次数最多的是 ...
- UVA 11235 Frequent values 线段树/RMQ
vjudge 上题目链接:UVA 11235 *******************************************************大白书上解释**************** ...
- POJ3368(Frequent values)--线段树
题目在这里 3368 Accepted 7312K 1829MS C++ 6936B 题意为给你一组数据,再给定一组区间,问你这个区间内出现次数最多的元素的次数是多少. 我还记得这题是学校校赛基础的题 ...
- HDOJ-1806 ( Frequent values ) 线段树区间合并
http://acm.hdu.edu.cn/showproblem.php?pid=1806 线段树维护区间出现频率最高的出现次数.为了维护上者,需要维护线段前后缀的出现次数,当和其他线段在端点处的字 ...
- hdu 1806 Frequent values 线段树
题目链接 给一个非递减数列, n个数, m个询问, 每个询问给出区间[L, R], 求这个区间里面出现次数最多的数的次数. 非递减数列, 这是最关键的一个条件... 需要保存一个区间最左边的数, 最右 ...
- 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)
Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...
- CodeChef - ANDMIN —— 线段树 (结点最多被修改的次数)
题目链接:https://vjudge.net/problem/CodeChef-ANDMIN Read problems statements in Mandarin Chinese, Russia ...
- hdu 4983 线段树+斐波那契数
http://acm.hdu.edu.cn/showproblem.php?pid=4893 三种操作: 1 k d, 修改k的为值增加d 2 l r, 查询l到r的区间和 3 l r, 从l到r区间 ...
- HYSBZ - 2243 树链剖分 + 线段树 处理树上颜色段数
用线段树处理颜色段数 记录区间内的颜色段数,区间右端点的颜色,区间右端点的颜色. int tr[maxn<<2], lc[maxn<<2], rc[maxn<<2] ...
随机推荐
- python学习之assert语句
assert语句用于代码检测并报警. 语法 assert code... 例子 # -*- coding: utf-8 -*- # assert语句说明 a,b= 1,23 a == 2 assert ...
- 基于Java Mina 和Netty 通信框架的JT/T809转发服务器设计
Apache MINA 是 Apache 组织的一个开源项目,为开发高性能和高可用性的网络应用程序提供了非常便利的框架. 也是Java开发者的一个福利(.NET目前还没有类似封装的这么好的基础sock ...
- Ubuntu 12.04 server 如何安装 OpenERP 7(转)
不经意的一次看到OpenERP这个开源ERP,就被其丰富的功能,简洁的画面,熟悉的语言所吸引.迫不及待的多方查询资料,自己架设一个测试环境来进行了解.以下为测试安装时候的步骤说明,以备查询,并供有需要 ...
- sublime text 格式化html css 与显示函数列表
sublime 格式化html css 1.ctrl + shift + p 2.输入install package,选择install package 3.输入:HTML-CSS-JS Pretti ...
- python 多线程糗事百科案例
案例要求参考上一个糗事百科单进程案例 Queue(队列对象) Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式 python下多线程的思考 ...
- SSH框架-Struts2基础-Action
Struts2的目录结构: 解压apps目录下的struts2-blank.war: 仿照这个最基本的项目,拷贝相关文件: 1.拷贝apps/struts2-blank/WEB-INF/classes ...
- 【vijos】1750 建房子(线段树套线段树+前缀和)
https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad ...
- (转)数据库ACID特性
转自:http://blog.csdn.net/shuaihj/article/details/14163713 隔离级别实现原理:http://www.cnblogs.com/wrencai/p/5 ...
- 【Raspberry Pi】修改时区
Raspberry Pi没有时钟模块,所以每次断电都会丢失时间,但它有联网获取时间的预设.但要修改默认时区 http://outofmemory.cn/code-snippet/2899/shumei ...
- 【Raspberry pi】GPIO注意事项
1.GPIO编码的方法 第三列是树莓派板子上的自然编号(左边引脚为1-15,右边引脚为2-26),RPi.GPIO.setmode(GPIO.BOARD)采用这列编号 树莓派主芯片提供商Broadco ...