例题:http://poj.org/problem?id=2104
最近可能是念念不忘,必有回响吧,总是看到区间第k大的问题,第一次看到是在知乎上有人面试被弄懵了后来又多次在比赛中看到。以前大概是知道怎么解决但是没有实际操作过。直到昨天看到了POJ上的2104题,一个标准的区间第K大询问,然后好好总结了一下区间第K大的问题。
普通人要是没想过这个问题,突然被问到第一个反应肯定和知乎上面试的哥们儿一样,把区间里面的所有数拎出来,排序,找第K个,但是这样时间复杂度是很大的,如果m次询问,时间复杂度是O( m×(n + n×logn) )要是询问次数m非常大时间复杂度很恐怖。
要是优化就有很多种方法,第一种就是利用分治的思维,分块。将n个数分成√n × logn 块,然后对每个块进行排序。既然是区间第K大,那假设N是区间内第K大的数,那么不大于N的数至少有K个。这样对N值进行二分枚举,每枚举出一个N值,然后去区间中找不大于N的数。因为对于每个块都是排好序的,所以如果该块完全包含在区间内,就直接对块进行二分查找不大于N的数有多少个。块部分包含在区间内的就直接暴力查找(过程如图1.1所示)。根据查找的值再扩大或者缩小N值。
图1.1
这个时候时间复杂度就是O( n×logn + m√nlog1.5n)
1 void init() { 2 scanf("%d%d",&n,&m);//n个数m次询问 3 unit = 1000;//分块大小 4 for(ll i=0;i<n;i++) { 5 scanf("%d",&num[i]); 6 OdrArr[i] = num[i]; 7 ve[i/unit].push_back(num[i]);//分别装入块中 8 } 9 for(ll i=0;i<n/unit;i++)//最后一个块不用排序 10 sort(ve[i].begin(),ve[i].end());//对每个块排序 11 sort(OdrArr, OdrArr+n);//二分枚举N值 12 }