4
29
2016
0

对众数问题的研究

总结出来有关众数的几个问题的算法,理解或有偏差,以后慢慢琢磨补充~

 

一、求众数的方法:

1、哈希表(数组存储,map映射)

2、排序后查找

3、利用平衡树(实际上map就是一棵红黑树)

treap为例,每个节点维护两个信息:

键值Key:保存元素值,

优先级Pro:保存元素在序列中出现的个数

BST维护Key,大根堆维护Pro

其实这种方法效率和普通bst是类似的。不过如果有求"出现次数第k多的数"

求所有众数或者删除某个数之后的众数(使用treap delete方法将节点下旋),这样的算法仍然是高效的。

具体分析Orz

http://blog.csdn.net/oiljt12138/article/details/50704236

 


 

二、区间众数问题(无修改):Orz sillycross

Violet6 蒲公英

长度为n的序列,元素大小1~n,区间众数

多次询问, 要求在线

n,q<=10^4

 

区间众数的离线算法

 

1、线段树维护区间每个数出现次数(满足区间减法)

单次询问转移代价:将区间[L,R]看作平面上点(L,R),每次转移代价即为两点间曼哈顿距离

询问转移的最小总代价:包含平面上所有(L,R)点==>平面图曼哈顿最小生成树O(nlogn)

 

可以证明转移次数为O(n^1.5+n^0.5*q)

则算法复杂度为O(n^1.5*logn+n^0.5*q*logn)

 

2、改进:平面图曼哈顿最小生成树不容易写..

求平面图曼哈顿最小生成树 实质:用可以接受的代价把所有点连接起来

 

在暴力方法基础上采取特殊的处理顺序,是常用的有力手法:

把横轴平均分成O(n^0.5)段,每段里直接按纵轴上升次序处理

(见左图)

保证了纵向不重复,横向重复少!

横向转移代价O(n^0.5*q)

纵向转移代价O(n^1.5)

则算法复杂度O((n+q)*n^0.5)

看来"分块解决"的思想效果显著啊


在线算法

合并所查询区间的答案复杂度高

 

对比离线算法,复杂度低是因为可由已知花费较小代价转移到所求

 

不妨预处理出一些区间的答案,将所查询区间尽量向已知答案的区间靠,代价取决于两个区间的相似性

 

预处理多少区间?预处理那些区间?

 

假设分成S,则每块大小为N/S.

序列长度N,元素集大小N,询问个数QN同阶

给出一个定理:

令集合为多重集合,即一个数可以出现多次

一个集合a的众数表示为mode(a)

那么考虑集合a,b,mode(ab)mode(a)b

形象点说:

如果区间 [L,R] 能覆盖区间 [L',R'] , 那么区间 [L,R] 的众数

或者就是区间 [L',R'] 的众数,

或者是位于 [L',R'] 之外但位于 [L,R] 内的某个数。

在这里:

答案要么是覆盖块的答案,要么是没被覆盖的元素

思路一:

预处理:

预处理每块:a[i][x] i块中x出现次数 O(S*N)

预处理连续多块:b[i][j][x] i~j块中x出现次数

b[i][j][x]=b[i][j-1][x]+a[j][x]   O(S^2*N)

并记录出现最多的数出现的次数!

处理询问:

整块O(1)查询,边界暴力O(N/S)

时间复杂度:

我们要让O(S^2*N)=O(N*N/S)

所以 S=O(N^(1/3))

则该算法O(N^(5/3))-O(N^(2/3))

 

思路二:

预处理:

c[i][j] 左端点是第i块起点,右端点是j的区间(即区间[st[i],j])的众数

不需要记录每个数分别出现的次数?!

O(N*S)

查询:

这次边界只有左边不到N/S个元素了

 

如何预处理和暴力查询??

新的问题:

给定一个序列 询问一个数在一段区间内出现的次数

  1. 把每个数出现的位置放在 vector set ,转化成求某个数是第几大

每次回答 O(lgN)

  1. F[i][x] 数字x[1,i]内出现的次数

对于每个数字,f[i]f[i-1]转移的过程 实质是修改一个数

进一步问题:

维护一个序列,要求支持:

1)修改一个数

2)查询第i次修改完成时某个数的值 (第 i 次修改完成后的序列就是 f[i] )

函数式数据结构嘛!!

函数式二叉树? O(log N)不行

函数式块状链表 !

假设分成 T .

修改的时候 T-1 块都可以重用,包含修改元素的那一块必须重新建.

于是时间复杂度 O(N/T+T)

取 T=N^0.5 做到 O(N^0.5)

每次修改 查询时可以直接找到第 X 次修改前的那个块状结构

每次找块O(1),查询众数要在块链中查询O(N/S)次

取 S=N^0.5 ,算法总时间复杂度: O(N^1.5) – O(N^0.5)

  1. 注意到当前子问题满足区间减法<"部分和"思想>

Query(L,R,x)=Query(1,R,x)-Query(1,L-1,x)

预处理所有Query(1,ed[i],x)的答案

对于具有特殊性的询问:询问的端点都是分块的端点 O(1)解决

算法复杂度:O(N^1.5) – O(N^0.5)

常数小,易实现,而且没有用到任何高级数据结构!!

 


 

三、问题扩展:带修改的区间众数 Orz CLJ

预处理:

x在第i个块内部的id

一个块内:A[b][i][x] b块的前i个数中x出现的次数

连续块中:C[i][x] i块中x出现的次数

(次数满足区间减法,故可由前缀得出区间次数)

Mx[i][j] i块到第j块中最大出现次数

Cnt[i] 出现了i次的值的个数

修改操作:pos位置的x值修改为y

C[i][x]--;C[i][y]++;(belong[pos]<=i<=n) 需要修改O(n^0.5)

维护O(S^2)对块之间的mx(最多改变1)

询问操作:同上,回答询问F(I,x)

考虑上述最终解法,可以每次O(1)回答[l,r]之间有几个x的询问

时间复杂度:

S=n^(1/3),而非S=n^(1/2)==>与朴素无异

预处理:O(n^(5/3))

修改:O(S^2)=O(n^(2/3))

询问:O(n/S)=O(n^(2/3))

则总时间复杂度:O((n+q)*n^(2/3))


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com