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] ...
随机推荐
- php socket 模型及效率问题
// 创建套接字 socket_create(); // 绑定 socket_bind(); // 监听 socket_listen(); // 主体, 死循环 while(true){ // sel ...
- js----Date\Math\数组对象
- vue 单文件组件
在很多vue项目中,我们使用vue.component来定义全局组件,紧接着用new Vue({el:'#container'})在每个页面内指定一个容器元素 这种方式在很多中小规模的项目中运作的很好 ...
- linux学习笔记32---命令ping和telnet
Linux系统的ping命令是常用的网络命令,它通常用来测试与目标主机的连通性,我们经常会说“ping一下某机器,看是不是开着”.不能打开网页时会说“你先ping网关地址192.168.1.1试试”. ...
- hdu 3652数位dp
/* 数位dp 题意:找到1-n之间包括13这个子串而且可以整除13的数 解:刚開始dp[N][N][2]这里的2用来记录是否为13表示当前位是否为13,我把上一位为1当前位为13和上一位部位1 这样 ...
- 球队以及得分计算的SQL语句
首先题目是这样的: 球队表teams 比赛表matches 赢了得3分,平局的得1分,输了得0分. 思路: 一个球队的成绩分为两部分,作为主队的得分和作为客队的得分: 计算出一次比赛中具体得了多少分, ...
- Cocos3.0 的android返回键功能实现
比如:Game.h Game.cpp 头文件Game.h中定义: void onKeyReleased(EventKeyboard::KeyCode keyCode,Event * pEvent) ...
- 第二百二十八节,jQuery EasyUI,TreeGrid(树形表格)组件
jQuery EasyUI,TreeGrid(树形表格)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 TreeGrid(树形表格)组件的使 ...
- 第二百零八节,jQuery EasyUI,SplitButton(分割按钮菜单)组件
jQuery EasyUI,SplitButton(分割按钮)组件 学习要点: 1.加载方式 2.属性列表 3.方法列表 本节课重点了解 EasyUI 中 SplitButton(分割按钮)组件的使用 ...
- Img src用base64数据
<img src='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgG ...