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-线段树求解出现最多的数的更多相关文章

  1. POJ 3368 Frequent values 线段树与RMQ解法

    题意:给出n个数的非递减序列,进行q次查询.每次查询给出两个数a,b,求出第a个数到第b个数之间数字的最大频数. 如序列:-1 -1 1 1 1 1 2 2 3 第2个数到第5个数之间出现次数最多的是 ...

  2. UVA 11235 Frequent values 线段树/RMQ

    vjudge 上题目链接:UVA 11235 *******************************************************大白书上解释**************** ...

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

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

  4. HDOJ-1806 ( Frequent values ) 线段树区间合并

    http://acm.hdu.edu.cn/showproblem.php?pid=1806 线段树维护区间出现频率最高的出现次数.为了维护上者,需要维护线段前后缀的出现次数,当和其他线段在端点处的字 ...

  5. hdu 1806 Frequent values 线段树

    题目链接 给一个非递减数列, n个数, m个询问, 每个询问给出区间[L, R], 求这个区间里面出现次数最多的数的次数. 非递减数列, 这是最关键的一个条件... 需要保存一个区间最左边的数, 最右 ...

  6. 计蒜客 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 ...

  7. CodeChef - ANDMIN —— 线段树 (结点最多被修改的次数)

    题目链接:https://vjudge.net/problem/CodeChef-ANDMIN Read problems statements in Mandarin Chinese, Russia ...

  8. 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区间 ...

  9. HYSBZ - 2243 树链剖分 + 线段树 处理树上颜色段数

    用线段树处理颜色段数 记录区间内的颜色段数,区间右端点的颜色,区间右端点的颜色. int tr[maxn<<2], lc[maxn<<2], rc[maxn<<2] ...

随机推荐

  1. python之histogram

    histogram A histogram is an accurate representation of the distribution of numerical data. It is an ...

  2. hdu5719 Arrange

    首先,根据题意可得B数组应是单调不升的,C数组是单调不降的. 可以发现A_1 = B_1 = C_1A​1​​=B​1​​=C​1​​,所以如果B_1 \neq C_1B​1​​≠C​1​​无解. 进 ...

  3. linux内核开机logo显示调试

    要使内核支持开机logo显示需要配置内核 配置如下: make menuconfig: Device Drivers  --->     Graphics support  --->    ...

  4. Windows 动态库创建和使用 part 2

    一.Windows动态库的创建: 1.先选择 "DLL" 和 “控项目” 2.添加一个头文件,一个源文件  CppDll.h,CppDll.cpp,一个模块定义文件 CppDll. ...

  5. textarea去掉边框

    <textarea style="BORDER-BOTTOM: 0px solid; BORDER-LEFT: 0px solid; BORDER-RIGHT: 0px solid; ...

  6. 安装loadrunner时出现”命令行选项语法错误键入命令 \?获得帮助“的解决方法

    安装LR11 时,安装Microsoft Visual c++2005 sp1运行时组件,就会提示命令行选项语法错误,键入“命令/?”可获取帮肋信息1.进入loadrunner-11\Addition ...

  7. URL编码,空格和+

    下表中列出了一些URL特殊符号及编码 + URL 中+号表示空格 %2B 空格 URL中的空格可以用+号或者编码 %20 / 分隔目录和子目录 %2F ? 分隔实际的URL和参数 %3F % 指定特殊 ...

  8. 网络协议之ts---ts 流分析详解

    名词缩写翻译: es:elements stream 基本流 pes:packet elements stream 打包流 pts:presentation time stamp 显示时间戳 dts: ...

  9. MATLAB使用fft求取给定音频信号的频率

    一段10s立体声音频,采样率位8000Hz,已知频率为1000Hz clc; clear; [data, Fs] = audioread('1khz_stereo_8000.wav'); fs=Fs; ...

  10. django 之 常用命令

    Django 基本命令 本节主要是为了让您了解一些django最基本的命令,请尝试着记住它们,并且多多练习下 1. 新建一个 django project django-admin.py startp ...