﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-一个能思想的人，才真是一个力量无边的人。 —— 巴尔扎克 -随笔分类-算法竞赛</title><link>http://www.cppblog.com/IronOxide/category/19844.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 29 Aug 2012 14:31:01 GMT</lastBuildDate><pubDate>Wed, 29 Aug 2012 14:31:01 GMT</pubDate><ttl>60</ttl><item><title>从第K元素看数据结构</title><link>http://www.cppblog.com/IronOxide/archive/2012/08/29/188669.html</link><dc:creator>IronOxide</dc:creator><author>IronOxide</author><pubDate>Wed, 29 Aug 2012 13:16:00 GMT</pubDate><guid>http://www.cppblog.com/IronOxide/archive/2012/08/29/188669.html</guid><wfw:comment>http://www.cppblog.com/IronOxide/comments/188669.html</wfw:comment><comments>http://www.cppblog.com/IronOxide/archive/2012/08/29/188669.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/IronOxide/comments/commentRss/188669.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/IronOxide/services/trackbacks/188669.html</trackback:ping><description><![CDATA[<p align="left"><span style="font-family: 宋体"><strong>声明：本文涉及的源代码及文章请点击<a href="http://www.cppblog.com/Files/IronOxide/从第K小元素看数据结构.zip"><span style="color: #3366ff">这里</span></a>下载，本文版权归IronOxide所有。博客地址：<a href="http://www.cppblog.com/IronOxide/"><span style="color: #3366ff">http://www.cppblog.com/IronOxide/</span></a><br /></strong>这篇文章讨论的是序列中第</span>K<span style="font-family: 宋体">大或第</span>K<span style="font-family: 宋体">小元素，由于第</span>K<span style="font-family: 宋体">大元素可以转化为求第</span>N-K+1<span style="font-family: 宋体">小元素（</span>N<span style="font-family: 宋体">为序列的长度），所以，本文专注于讨论第</span>K<span style="font-family: 宋体">小元素。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">本文讨论的几个问题：</span></p>
<p style="text-indent: -18pt; margin-left: 39pt"><span>1.<span style="font: 7pt 'Times New Roman'"> </span></span><span style="font-family: 宋体">对给定整数序列，求该序列中第</span>K<span style="font-family: 宋体">小的元素。</span></p>
<p style="text-indent: -18pt; margin-left: 39pt"><span>2.<span style="font: 7pt 'Times New Roman'"> </span></span><span style="font-family: 宋体">对某一整数序列，允许动态更改序列中的数。动态查询序列中第</span>K<span style="font-family: 宋体">小元素。</span></p>
<p style="text-indent: -18pt; margin-left: 39pt"><span>3.<span style="font: 7pt 'Times New Roman'"> </span></span><span style="font-family: 宋体">给定一个整数序列和若干个区间，回答该区间内第</span>K<span style="font-family: 宋体">小元素。</span></p>
<p style="text-indent: -18pt; margin-left: 39pt"><span>4.<span style="font: 7pt 'Times New Roman'"> </span></span><span style="font-family: 宋体">对某一整数序列，允许动态更改序列中的数。动态查询序列中的第</span>K<span style="font-family: 宋体">小元素。</span></p>
<p><span style="font-family: 宋体">【关键字】</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">第</span>K<span style="font-family: 宋体">小元素</span><span> </span><span style="font-family: 宋体">树状数组</span><span> </span><span style="font-family: 宋体">线段树</span> <span style="font-family: 宋体">平衡二叉树</span> <span style="font-family: 宋体">归并树</span><span> </span><span style="font-family: 宋体">划分树</span> <span style="font-family: 宋体">单调队列</span> <span style="font-family: 宋体">堆</span> <span style="font-family: 宋体">块状表</span> </p>
<p style="text-indent: 21.75pt"></p>
<p align="center"><strong><span style="font-family: 宋体; font-size: 24pt">【问题一】</span></strong><strong></strong></p>
<p><span></span></p>
<p><span></span><strong><u><span style="font-family: 宋体">问题描述</span></u></strong><span style="font-family: 宋体">：</span></p>
<p><span></span><span style="font-family: 宋体">给出一个乱序整数序列</span>a[1&#8230;n] <span style="font-family: 宋体">，求该序列中的第</span>K<span style="font-family: 宋体">小元素。（</span>1&lt;=K&lt;=N<span style="font-family: 宋体">）。</span></p>
<p><span></span></p>
<p><span></span><strong><u><span style="font-family: 宋体">算法分析</span></u></strong><span style="font-family: 宋体">：</span></p>
<p><span></span><span style="font-family: 宋体">用基于快速排序的分治算法，期望复杂度为</span>O(N)<span style="font-family: 宋体">。</span> </p>
<p><span></span><strong><u><span style="font-family: 宋体">代码</span></u></strong><span style="font-family: 宋体">：</span><span> </p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;qs(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">a&nbsp;,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;l&nbsp;,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;r&nbsp;,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;k){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(l&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;r)&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;a[l]&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;l&nbsp;,&nbsp;j&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;r&nbsp;,&nbsp;x&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;a[(l</span><span style="color: #000000">+</span><span style="color: #000000">r)</span><span style="color: #000000">&gt;&gt;</span><span style="color: #000000">1</span><span style="color: #000000">]&nbsp;,&nbsp;temp&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">do</span><span style="color: #000000">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">while</span><span style="color: #000000">(a[i]&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;x)&nbsp;</span><span style="color: #000000">++</span><span style="color: #000000">&nbsp;i&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">while</span><span style="color: #000000">(a[j]&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;x)&nbsp;</span><span style="color: #000000">--</span><span style="color: #000000">&nbsp;j&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(i&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;j){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;temp&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;a[i]&nbsp;;&nbsp;&nbsp;a[i]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;a[j]&nbsp;,&nbsp;&nbsp;a[j]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;temp&nbsp;;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i</span><span style="color: #000000">++</span><span style="color: #000000">&nbsp;;&nbsp;&nbsp;j</span><span style="color: #000000">--</span><span style="color: #000000">&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000ff">while</span><span style="color: #000000">(i</span><span style="color: #000000">&lt;=</span><span style="color: #000000">j)&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(k&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;j)&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;qs(a&nbsp;,&nbsp;l&nbsp;,&nbsp;j&nbsp;,&nbsp;k);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(k&nbsp;</span><span style="color: #000000">&gt;=</span><span style="color: #000000">&nbsp;i)&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;qs(a&nbsp;,&nbsp;i&nbsp;,&nbsp;r&nbsp;,&nbsp;k);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;x&nbsp;;<br />}<br /></span></div>
<p></span>&nbsp;</p>
<p><strong><span></span></strong><strong><u><span style="font-family: 宋体"></p>
<p>练习</span></u></strong></p>
<p><span><a href="http://www.rqnoj.cn/Problem_350.html"><span style="color: #3366ff">RQNOJ 350</span></a> </span><span style="font-family: 宋体">这题数据量比较小</span>1&#8804;N&#8804;10000,1&#8804;M&#8804;2000 <span style="font-family: 宋体">。所以计算量不会超过</span>10^7<span style="font-family: 宋体">。当然用到后面的归并树或划分树，能将复杂度降低。</span></p>
<p>&nbsp;</p>
<p align="center"><span style="font-family: 宋体; font-size: 24pt">【问题二】</span></p>
<p><span></span><strong><u><span style="font-family: 宋体">问题描述</span></u></strong><span style="font-family: 宋体">：</span></p>
<p><span></span><span style="font-family: 宋体">给出一个乱序整数序列</span>a[1...n] <span style="font-family: 宋体">，有</span>3<span style="font-family: 宋体">种操作：</span></p>
<p style="text-indent: 21pt" align="left"><span style="font-family: 宋体">操作一：</span>ADD<span> NUM </span><span style="font-family: 宋体">往序列添加一个数</span>NUM<span style="font-family: 宋体">。</span><br /><span></span><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 操作二：</span>DEL<span> NUM </span><span style="font-family: 宋体">从序列中删除一个数</span>NUM<span style="font-family: 宋体">（若有多个，只删除一个）。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">操作三：</span>QUERY K<span> </span><span style="font-family: 宋体">询问当前序列中第</span>K<span style="font-family: 宋体">小的数。</span></p>
<p><span></span><span style="font-family: 宋体">输出每次询问的数。假设操作的次数为</span>M<span style="font-family: 宋体">。</span></p>
<p><span></span></p>
<p><span></span><strong><u><span style="font-family: 宋体">算法分析：</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">这题实际上就是一边动态增删点，一边查询第</span>K<span style="font-family: 宋体">小数。这类题有两种思维方法：一是二分答案，对当前测试值</span>mid<span style="font-family: 宋体">，查询</span>mid<span style="font-family: 宋体">在当前序列中的排名</span>rank <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">然后根据</span>rank<span style="font-family: 宋体">决定向左边还是右边继续二分。另一种是直接求第</span>K<span style="font-family: 宋体">小元素。</span></p>
<p><span></span><span style="font-family: 宋体">这个题可以用各种类型的数据结构解决，其时间复杂度和编程复杂度稍有区别：</span></p>
<p><span></span><strong><span style="font-family: 宋体">线段树：</span></strong><span style="font-family: 宋体">运用第一种思维，当添加（删除）一个数</span>x<span style="font-family: 宋体">时，相当于往线段树上添加（删除）一条</span>(x , maxlen)<span style="font-family: 宋体">（注意是闭区间）长度的线段。这样询问时，覆盖</span>[mid , mid]<span style="font-family: 宋体">区间的线段数就是比</span>mid<span style="font-family: 宋体">小的数，加上</span>1<span style="font-family: 宋体">就是</span>rank<span style="font-family: 宋体">。二分次数为</span>log(maxlen) <span style="font-family: 宋体">，查一次</span>mid<span style="font-family: 宋体">的</span>rank <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">复杂度为</span>O(logN) <span style="font-family: 宋体">。所以总复杂度上界为</span>O(M*logN*logN) <span style="font-family: 宋体">。为方便比较，这里认为</span>log(maxlen)<span style="font-family: 宋体">等于</span>logN<span style="font-family: 宋体">。</span><strong></strong></p>
<p><strong><span></span></strong><strong><span style="font-family: 宋体">树状数组：</span></strong></p>
<p><strong><span></span></strong><span style="font-family: 宋体">第一种思维：这个相对简单，因为树状数组求比</span>mid<span style="font-family: 宋体">小的数就一个</span>getsum(mid-1)<span style="font-family: 宋体">就搞定。</span></p>
<p><span style="font-family: 宋体">复杂度同线段树一样。只是常数很小，代码量也很小。</span></p>
<p><strong><span></span></strong><span style="font-family: 宋体">第二种思维：我只能说很巧妙。回顾树状数组求和时的操作：</span></p>
<p><span></span><strong><u><span style="font-family: 宋体">代码</span></u></strong></p>
<p><span></span></p>
<p><span></span><span style="font-family: 宋体"></p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-family: Courier; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; text-decoration: ; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;getsum(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;x&nbsp;){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;res&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">(&nbsp;;&nbsp;x</span><span style="color: #000000">&gt;</span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;;&nbsp;x</span><span style="color: #000000">-=</span><span style="color: #000000">lowbit(x)&nbsp;)&nbsp;&nbsp;res&nbsp;</span><span style="color: #000000">+=</span><span style="color: #000000">&nbsp;arr[x]&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;res&nbsp;;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;}</span></div>
<p><br />对二进制数</span>10100010 <span style="font-family: 宋体">是依次累加</span>arr[10100010] , arr[10100000] , arr[10000000] <span style="font-family: 宋体">。从而得到小于</span>x<span style="font-family: 宋体">的数的个数。当反过来看的时候，就有了这种方法：从高位到低位依次确定答案的当前为是</span>0<span style="font-family: 宋体">还是</span>1<span style="font-family: 宋体">，首先假设是</span>1<span style="font-family: 宋体">，判断累计结果是否会超过</span>K<span style="font-family: 宋体">，超过</span>K<span style="font-family: 宋体">则假设不成立，应为</span>0<span style="font-family: 宋体">，否则继续确定下一位。看程序就明白了。</span></p>
<p><span></span><strong><u><span style="font-family: 宋体">代码</span></u></strong><span style="font-family: 宋体"></p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-family: Courier; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; text-decoration: ; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;getkth(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;k){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;ans&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;,&nbsp;cnt&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;,&nbsp;i&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">(i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">20</span><span style="color: #000000">&nbsp;;&nbsp;i</span><span style="color: #000000">&gt;=</span><span style="color: #000000">0</span><span style="color: #000000">&nbsp;;&nbsp;</span><span style="color: #000000">--</span><span style="color: #000000">i){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ans&nbsp;</span><span style="color: #000000">+=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">i&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(ans</span><span style="color: #000000">&gt;=</span><span style="color: #000000">maxn</span><span style="color: #000000">||</span><span style="color: #000000">cnt</span><span style="color: #000000">+</span><span style="color: #000000">c[ans]</span><span style="color: #000000">&gt;=</span><span style="color: #000000">k)&nbsp;ans</span><span style="color: #000000">-=</span><span style="color: #000000">1</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">i&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;cnt&nbsp;</span><span style="color: #000000">+=</span><span style="color: #000000">c[ans]&nbsp;;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;ans</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span></div>
<p><br />复杂度：自然就比第一种少了一个阶。</span>O(M*logN)<span style="font-family: 宋体">。</span></p>
<p><strong><span></span></strong><strong><span style="font-family: 宋体">平衡二叉树：</span></strong></p>
<p><strong><span></span></strong><span style="font-family: 宋体">各种平衡二叉树都可以解决这个问题：</span>Size Balance Tree <span style="font-family: 宋体">，</span>Spaly <span style="font-family: 宋体">，</span>Treap <span style="font-family: 宋体">，红黑树等等。</span> <span style="font-family: 宋体">不得不说，</span>Size Balance Tree<span style="font-family: 宋体">解这个问题是比较方便的。因为</span>SBT<span style="font-family: 宋体">本身就有一个</span>Select<span style="font-family: 宋体">操作，直接调用一下，就出来了。</span><span> </span></p>
<p><span></span><strong><u><span style="font-family: 宋体">代码</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">复杂度：用的是第二种思维。</span>O(M*logN)<span style="font-family: 宋体">。</span></p>
<p><span></span></p>
<p><span></span><strong><u><span style="font-family: 宋体">总结：</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">其实，综合起来，各种数据结构，各种算法</span> <span style="font-family: 宋体">，各种纠结。平衡树太复杂，线段树又高了点（一般情况不会有问题的）。最好的方法还是<strong><u>树状数组的二进制算法</u></strong>，时间复杂度和编程复杂度达到双赢。</span></p>
<p><span></span><span style="font-family: 宋体">但是，总结起来，发现线段树或者树状数组所消耗的空间跟数据的范围有关，当序列元素是浮点数或者范围很大时，就有点力不从心了（当然，离线的情况可以离散化，在线的某些情况可以离散化），而用平衡二叉树就不存在这样的问题。原来<strong><u>平衡二叉树才是王道</u></strong>。</span></p>
<p><span></span></p>
<p><span></span><strong><u><span style="font-family: 宋体">练习：</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">最近发现，基于这种思想的题目还真是不少啊</span>~ ~ <a href="http://poj.org/problem?id=3481"><span style="color: #3366ff">POJ Double Queue</span></a></p>
<p><span><a href="http://61.187.179.132/JudgeOnline/problem.php?id=1503"><span style="color: #3366ff">[NOI2004]</span><span style="font-family: 宋体; color: #3366ff">郁闷的出纳员</span></a> </span><span style="font-family: 宋体">工资反过来看，职员工资不变，而是工资下界在变而已。当降低工资下界时，为了知道哪些职员走人了，我还用了个二叉堆。。。。。</span></p>
<p><span><a href="http://poj.org/problem?id=2761"><span style="color: #3366ff">POJ 2761 Feed the dogs</span></a> </span><span style="font-family: 宋体">首先该题用接下来<strong><u>问题</u></strong></span><strong><u>3</u></strong><span style="font-family: 宋体">的一个特例。但其特殊性在于任意两个区间不包含，导致把区间按左端点（不会存在相同左端点滴，否则必包含）排序之后，依次扫描每个区间，当前区间和前一个区间相交的部分不动，前一区间有而当前区间没有的部分删除，前一区间没有而当前区间有的部分添加。这能保证每个元素正好添删各一次。</span></p>
<p><span><a href="http://poj.org/problem?id=2823"><span style="color: #3366ff">POJ 2823 Sliding Window</span></a> </span><span style="font-family: 宋体">太特殊了，最大值就是第</span>K <span style="font-family: 宋体">小</span> <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">最小值就是第</span>1<span style="font-family: 宋体">小（这是一个很重要的启示：<strong><u><span style="color: black">树状数组也可以动态求最值</span></u></strong>）。<strong><u>单调队列</u></strong>可以弄成线性算法。</span></p>
<p align="left"><span><a href="http://acm.hunnu.edu.cn/online/?action=problem&amp;type=show&amp;id=10571"><span style="color: #3366ff">HUNNU 10571 Counting Girls </span></a></span><strong><u><span style="font-family: 宋体">第四届华中南邀请赛</span></u></strong> <span style="font-family: 宋体">但数据与题目稍有不符</span> <span style="font-family: 宋体">但可以确定每个数大于等于</span>0<span style="font-family: 宋体">且不超过</span>200000 <span style="font-family: 宋体">。关键是求第</span>X th <span style="font-family: 宋体">到第</span> Y th <span style="font-family: 宋体">个</span>MM<span style="font-family: 宋体">的</span>rating <span style="font-family: 宋体">和要注意下。</span></p>
<p><a href="http://acm.hdu.edu.cn/showproblem.php?pid=2852"><span style="color: #3366ff">KiKi's K-Number</span></a> <span style="font-family: 宋体">这题就没什么好说的了。求</span>[a+1,MaxnInt]<span style="font-family: 宋体">的第</span>K <span style="font-family: 宋体">小的数，先求出</span>[0,a]<span style="font-family: 宋体">有多少个数，设为</span>cnt<span style="font-family: 宋体">，只要求第</span>K+cnt<span style="font-family: 宋体">小的数就可以了。哦，题目说是求第</span>K<span style="font-family: 宋体">大的，实际是求第</span>K<span style="font-family: 宋体">小的，这有点意思</span>~</p>
<p>&nbsp;</p>
<p align="center"><span style="font-family: 宋体; font-size: 24pt">【问题三】</span></p>
<p><strong><u><span style="font-family: 宋体">问题描述</span></u></strong> </p>
<p><span style="font-family: 宋体">给定一个序列</span>a[1...n] <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">有</span>m <span style="font-family: 宋体">个询问</span> <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">每次询问</span>a[i...j]<span style="font-family: 宋体">之间第</span>K<span style="font-family: 宋体">小的数。</span></p>
<p><span style="font-family: 宋体">（引用一句英文：</span>You can assume that n&lt;100001 and m&lt;50001<span style="font-family: 宋体">）</span> </p>
<p><strong><u><span style="font-family: 宋体">算法分析</span></u></strong></p>
<p><strong><u><span style="font-family: 宋体">块状表</span></u></strong> </p>
<p><span style="font-family: 宋体">如果这道题能够想到用块状表的话，思维复杂度和编程复杂度都不高</span>~<span style="font-family: 宋体">，考虑这样操作：</span></p>
<p><span style="font-family: 宋体">首先预处理，将序列划分成</span>sqrt(N)<span style="font-family: 宋体">个小段，每段长</span>[sqrt(N)] <span style="font-family: 宋体">，划分时，将每小段排好序。</span></p>
<p><span style="font-family: 宋体">然后就是查询了，对区间</span>[i,j]<span style="font-family: 宋体">的查询，同样采用二分求比测试值</span>mid<span style="font-family: 宋体">小的个数。</span>i<span style="font-family: 宋体">和</span>j<span style="font-family: 宋体">所在的零散的两小段直接枚举求，中间完整的小段则二分查找求。<br />这样一次查询时间复杂度为<br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula5.PNG" width="374" height="64" /><br /><br /></span><span style="font-family: 宋体">于是总复杂度为：</span> 
<p><span></span></p><span style="font-family: 宋体"><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula6.PNG" width="401" height="59" /><br />当然，这里计算量是比较大的，实际写了个程序也是超时的。但当</span>M<span style="font-family: 宋体">比较小时，也未尝不是一种好的选择（或者当开阔思路吧，但对<strong><u>问题四</u></strong>却正好打个擦边球）。</span> 
<p>&nbsp;</p>
<p><strong><u><span style="font-family: 宋体">划分树</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">划分树应该是解决这道题复杂度最低的方法，复杂度为<br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula7.PNG" width="305" height="56" /><br /></span><span></span><span style="font-family: 宋体">思想其实很简答，用线段树把整个序列分成若干区间。建树时，对区间</span>[l,r]<span style="font-family: 宋体">划分：选择该区间的中位数</span>value<span style="font-family: 宋体">（注意：可以先用快速排序对原来序列排个序，于是可以速度得到中位数），将小于等于</span>value<span style="font-family: 宋体">的不超过</span>mid-l+1<span style="font-family: 宋体">个数划分到</span>[l,mid]<span style="font-family: 宋体">区间，其余划分到</span>[mid+1,r]<span style="font-family: 宋体">区间，用一个数组把每层划分后的序列保存起来。</span></p>
<p><span></span></p>
<p><span></span><span style="font-family: 宋体">然后，查找的时候，</span>Find(x , l , r , k)<span style="font-family: 宋体">表示超找</span>x<span style="font-family: 宋体">节点内区间</span>[l,r]<span style="font-family: 宋体">第</span>K<span style="font-family: 宋体">小的数。将该节点区间分成三个区间</span>[seg_left , l-1] <span style="font-family: 宋体">，</span> [l , r ] , [r+1 , seg_right]<span style="font-family: 宋体">来讨论问题，他们在划分过程中分到</span>[l,mid]<span style="font-family: 宋体">区间的个数依次为</span>ls , ms , rs <span style="font-family: 宋体">。若</span>ms&lt;=K<span style="font-family: 宋体">自然查左边区间</span>, Find(2*x , l+ls , l+ls+ms-1,K)<span style="font-family: 宋体">。否则自然查右边，计算下标很烦啊。有代码在</span>~</p>
<p><strong><u><span style="font-family: 宋体">代码</span></u></strong> <br /><span style="font-family: 宋体"></p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-family: Courier; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; text-decoration: ; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;build(lld&nbsp;d&nbsp;,lld&nbsp;l&nbsp;,&nbsp;lld&nbsp;r&nbsp;){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(l&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;r)&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lld&nbsp;i&nbsp;,&nbsp;mid&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;(l</span><span style="color: #000000">+</span><span style="color: #000000">r)</span><span style="color: #000000">&gt;&gt;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,&nbsp;j</span><span style="color: #000000">=</span><span style="color: #000000">l&nbsp;,&nbsp;k</span><span style="color: #000000">=</span><span style="color: #000000">mid</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">(i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;l&nbsp;;&nbsp;i&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;r&nbsp;;&nbsp;</span><span style="color: #000000">++</span><span style="color: #000000">&nbsp;i){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s[d][i]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;s[d][i</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">]&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(tr[d][i]&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;mid){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s[d][i]</span><span style="color: #000000">++</span><span style="color: #000000">&nbsp;;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tr[d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">][j</span><span style="color: #000000">++</span><span style="color: #000000">]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;tr[d][i];&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="color: #0000ff">else</span><span style="color: #000000">{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tr[d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">][k</span><span style="color: #000000">++</span><span style="color: #000000">]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;tr[d][i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;build(d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,l&nbsp;,&nbsp;mid);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;build(d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,&nbsp;mid</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,&nbsp;r);<br />}<br /><br />lld&nbsp;getkth(lld&nbsp;d&nbsp;,lld&nbsp;lp&nbsp;,lld&nbsp;rp&nbsp;,&nbsp;lld&nbsp;l&nbsp;,&nbsp;lld&nbsp;r&nbsp;,&nbsp;lld&nbsp;k){<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(lp&nbsp;</span><span style="color: #000000">==</span><span style="color: #000000">&nbsp;rp&nbsp;)&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;tr[d][lp]&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;lld&nbsp;mid&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;(lp&nbsp;</span><span style="color: #000000">+</span><span style="color: #000000">&nbsp;rp)</span><span style="color: #000000">&gt;&gt;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(k</span><span style="color: #000000">&lt;=</span><span style="color: #000000">s[d][r]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][l</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">])<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;getkth(d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,lp&nbsp;,&nbsp;mid&nbsp;,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lp</span><span style="color: #000000">+</span><span style="color: #000000">s[d][l</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][lp</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">]&nbsp;,&nbsp;lp</span><span style="color: #000000">+</span><span style="color: #000000">s[d][r]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][lp</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">]</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,&nbsp;k&nbsp;);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000">&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;getkth(d</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,mid</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;,&nbsp;rp&nbsp;,&nbsp;mid</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">+</span><span style="color: #000000">(l</span><span style="color: #000000">-</span><span style="color: #000000">lp)</span><span style="color: #000000">-</span><span style="color: #000000">(s[d][l</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][lp</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">])&nbsp;,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mid</span><span style="color: #000000">+</span><span style="color: #000000">(r</span><span style="color: #000000">-</span><span style="color: #000000">lp</span><span style="color: #000000">+</span><span style="color: #000000">1</span><span style="color: #000000">)</span><span style="color: #000000">-</span><span style="color: #000000">(s[d][r]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][lp</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">])&nbsp;,&nbsp;k</span><span style="color: #000000">-</span><span style="color: #000000">(s[d][r]</span><span style="color: #000000">-</span><span style="color: #000000">s[d][l</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">])&nbsp;);<br />}<br /></span></div>
<p><br /><strong><u>归并树</u></strong></span></p>
<p><span></span><span style="font-family: 宋体">归并树思想就跟简单了，说白了就是线段树每个区间</span>[l,r]<span style="font-family: 宋体">内的数都排好序然后保存起来，</span> <span style="font-family: 宋体">从两个儿子到父亲节点，其实就是<strong><u>两个有序序列归并成一个有序序列</u></strong>，所以就称归并树了。</span></p>
<p><span></span><span style="font-family: 宋体">用前面说的二分答案，对测试值</span>mid<span style="font-family: 宋体">求</span>rank<span style="font-family: 宋体">。查找的时候，将查找区间划分成线段树中若干子区间之并，很明显各个子区间小于</span>mid<span style="font-family: 宋体">的个数加起来，就是该区间小于</span>mid<span style="font-family: 宋体">的个数。而每个子区间又是有序的，所有二分可以很快找到小区间小于</span>mid<span style="font-family: 宋体">的个数。</span></p>
<p><span></span><span style="font-family: 宋体">总结起来，有三次二分：</span></p>
<p><span>1.</span><span style="font-family: 宋体">二分答案；</span></p>
<p><span>2.</span><span style="font-family: 宋体">查找区间</span>[a,b]<span style="font-family: 宋体">划分成不超过</span>log(b - a)<span style="font-family: 宋体">个小区间；</span></p>
<p><span>3.</span><span style="font-family: 宋体">对每个子区间，二分查找小于</span>mid<span style="font-family: 宋体">的个数；</span></p>
<p><span></span></p>
<p><span style="font-family: 宋体">于是，整个算法复杂度为：<br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula8.PNG" width="416" height="66" /><br /></span><strong><u><span style="font-family: 宋体">总结</span></u></strong><span style="font-family: 宋体">：</span></p>
<p><span style="font-family: 宋体">对本问题，提供了三种算法，其中基于快排的划分算法是最快的；块状表思维和编程都比较简单，但是复杂度比较高；归并树算法思想很好，二分答案，求</span>rank <span style="font-family: 宋体">，</span> <span style="font-family: 宋体">后面<strong><u>问题四</u></strong>的算法就是根据这种思维设计出来的。</span><span> </span></p>
<p><strong><u><span style="font-family: 宋体">练习</span></u></strong></p>
<p><a href="http://poj.org/problem?id=2761"><span style="color: #3366ff">POJ 2761 Feed the dogs</span></a><strong><u></u></strong></p>
<p><a href="http://poj.org/problem?id=2104"><span style="color: #3366ff">POJ 2104 K-th Number </span></a><strong><u></u></strong></p>
<p><a href="http://www.rqnoj.cn/Problem_560.html"><span style="color: #3366ff">NOI 2010 <span style="font-family: 宋体; color: #3366ff">超级刚琴</span></span></a> <span style="font-family: 宋体">这题还真有点难度。</span> </p>
<p><span style="font-family: 宋体">思路：划分树</span>+<span style="font-family: 宋体">堆</span>+<span style="font-family: 宋体">单调队列</span> </p>
<p><span></span><span style="font-family: 宋体">对每个区间，起点为</span>i+1,<span style="font-family: 宋体">终点在区间</span>[i+L,i+R]<span style="font-family: 宋体">。计</span>s[i]=a[0]+a[1]+...+a[i] (<span style="font-family: 宋体">规定</span>a[0]=0), <span style="font-family: 宋体">设</span>opt[i,k]<span style="font-family: 宋体">表示第</span>k<span style="font-family: 宋体">大的</span>s[j] (i+L&lt;=j&lt;=i+R)<span style="font-family: 宋体">，即</span>opt[i,k] = Max_k { s[j] | i+L&lt;=j&lt;=i+R} <span style="font-family: 宋体">（</span>Max_k<span style="font-family: 宋体">表示第</span>k<span style="font-family: 宋体">大）。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">先进行两个预处理工作：</span></p>
<p style="text-indent: 21pt; margin-left: 0cm"><span>1.</span><span style="font-family: 宋体">将</span>s[1] , s[2] , s[3] , .. s[n] <span style="font-family: 宋体">建成一颗静态划分树。</span></p>
<p style="text-indent: 21pt; margin-left: 0cm"><span>2.</span><span style="font-family: 宋体">用单调队列预处理出所有</span>opt[i,1]<span style="font-family: 宋体">的值，并将所有</span>opt[i,1]-s[i]<span style="font-family: 宋体">用一个堆维护起来。当然也可以直接通过查询</span>[i+L,i+R]<span style="font-family: 宋体">区间第</span>1<span style="font-family: 宋体">大的值来预处理</span>opt[i,1]<span style="font-family: 宋体">。</span></p>
<p><span></span><span style="font-family: 宋体">下面开始取值，并更新：依次从堆中取出最大值，设是</span>opt[i,j]-s[i]<span style="font-family: 宋体">，把它累加至答案，再查询划分树中</span>[i+L , i+R]<span style="font-family: 宋体">区间第</span>j+1<span style="font-family: 宋体">大的值</span>opt[i,j+1] , <span style="font-family: 宋体">将</span>opt[i,j+1]-s[i]<span style="font-family: 宋体">后入堆。直到累加次数为</span>K<span style="font-family: 宋体">停止。</span></p>
<p>&nbsp;</p>
<p align="center"><strong><span style="font-family: 宋体; font-size: 24pt">【</span></strong><span style="font-family: 宋体; font-size: 24pt">问题四<strong>】</strong></span><strong></strong></p>
<p><strong><u><span style="font-family: 宋体">问题描述</span></u></strong></p>
<p><span style="font-family: 宋体">给定一个原始序列</span>a[1...n] , <span style="font-family: 宋体">有两种操作：</span></p>
<p><span style="font-family: 宋体">操作一：</span> QUERY i j k <span style="font-family: 宋体">询问当前序列中，</span>a[i...j]<span style="font-family: 宋体">之间第</span>k<span style="font-family: 宋体">小的数是多少</span> </p>
<p><span style="font-family: 宋体">操作二：</span> CHANG I T <span style="font-family: 宋体">将</span>a[i]<span style="font-family: 宋体">改为</span>T <span style="font-family: 宋体">；</span></p>
<p><span style="font-family: 宋体">输出每次询问的结果。</span>N&lt;=50000 , <span style="font-family: 宋体">操作次数</span>M&lt;=10000 <br /></p>
<p><strong><u><span style="font-family: 宋体">算法分析</span></u></strong></p>
<p><strong><u><span style="font-family: 宋体">块状表</span></u></strong></p>
<p><span></span><span style="font-family: 宋体">将</span>a[1...n]<span style="font-family: 宋体">分成</span>sqrt(N)<span style="font-family: 宋体">段，每段长</span>[sqrt(N)],<span style="font-family: 宋体">为方便二分查找每段，将每段排好序，对操作二，先删去</span>a[i] , <span style="font-family: 宋体">在插入</span>T , <span style="font-family: 宋体">维护该块得有序性，复杂度为</span>O(sqrt(N))<span style="font-family: 宋体">。</span></p>
<p><span></span><span style="font-family: 宋体">对操作一，二分答案，设当前测试值为</span>mid , <span style="font-family: 宋体">先统计两端零散块，</span>O(2*sqrt(N)) <span style="font-family: 宋体">。对中间完整块，每块二分查找，总复杂度为：<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula4.PNG" width="422" height="79" /><br /><br /></span>
<p><span></span></p><strong><u><span style="font-family: 宋体"><font face="Courier New"></font>线段树</span>+<span style="font-family: 宋体">平衡二叉树</span></u></strong> 
<p>&nbsp;</p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">序列被线段树划分为区间节点，而每个节点又是一颗平衡二叉树，平衡二叉树放的是该区间段的所有数。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">对操作二，依次更新从跟区间到叶子节点区间的平衡树即可（先删除</span>a[i],<span style="font-family: 宋体">在插入</span>T<span style="font-family: 宋体">），考虑复杂度，第一层规模为</span>N<span style="font-family: 宋体">，第二层规模为</span>N/2<span style="font-family: 宋体">，第</span>k<span style="font-family: 宋体">层规模为</span> N/2^k <span style="font-family: 宋体">。进行依次操作二的复杂度为：<br />&nbsp;&nbsp;&nbsp;&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula1.PNG" width="793" height="91" /><br />&nbsp;&nbsp;&nbsp; 这里 K = [logN]<br /></span><span style="font-family: 宋体">对操作一：询问区间被划分为不超过</span>[logN]<span style="font-family: 宋体">个线段树节点之并，每次区间查找上界为</span>logN <span style="font-family: 宋体">。所以，对每个测试值</span>mid , <span style="font-family: 宋体">耗时</span>(logN)^2 , <span style="font-family: 宋体">故查询一次的复杂度为：<br />&nbsp;&nbsp;&nbsp; <img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula2.PNG" width="267" height="58" /><br /></span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">总复杂度为</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/ironoxide/formula3.PNG" width="299" height="59" /></p>
<p style="text-indent: 21pt"></p>
<p><strong><u><span style="font-family: 宋体">练习</span></u></strong></p>
<p><a href="http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112"><span style="color: #3366ff">ZOJ 2112 Dynamic Rankings</span></a></p><img src ="http://www.cppblog.com/IronOxide/aggbug/188669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/IronOxide/" target="_blank">IronOxide</a> 2012-08-29 21:16 <a href="http://www.cppblog.com/IronOxide/archive/2012/08/29/188669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>