POJ2104 K-th Number 静态区间第k最值 平方分割

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ2104 K-th Number 静态区间第k最值 平方分割相关的知识,希望对你有一定的参考价值。

干掉这道题的那一刻,我只想说:我终于**的AC了!!!

最终内存1344K,耗时10282ms,比起归并树、划分树以及其他各种黑科技,这个成绩并不算光彩⊙﹏⊙

但至少,从最初的无数次TLE到最终的AC,这过程见证了一个二分算法的艰辛优化

先贴代码:

技术分享
 1 const int bktSize=1024;
 2 const int bktMaxIdx=bktSize-1;
 3 const int bktCount=128;
 4 const int bktDigit=10;
 5 const int maxV=1e9;
 6 
 7 int bucket[bktCount][bktSize];
 8 int unOrdered[bktSize*bktCount];
 9 int ordered[bktSize*bktCount];
10 int N,K;
11 
12 #include <cstdio>
13 #include <cstring>
14 #include <algorithm>
15 
16 void init()
17 {
18     scanf("%d%d",&N,&K);
19     memset(bucket[N>>bktDigit],0x7f,sizeof(bucket[N>>bktDigit]));
20     for(int i=0;i<N;i++) 
21     {
22         scanf("%d",unOrdered+i);
23         ordered[i]=unOrdered[i];
24         bucket[i>>bktDigit][i&bktMaxIdx]=unOrdered[i];
25     }
26     
27     using std::sort;
28     int bktUsed=N>>bktDigit;
29     sort(ordered,ordered+N);
30     for(int i=0;i<=bktUsed;i++) sort(bucket[i],bucket[i]+bktSize);
31 }
32 
33 inline void enumerate(int _rangeL,int _rangeR,int _val,int& _notMore)
34 {
35     for(int i=_rangeL;i<=_rangeR;i++)
36         if(unOrdered[i]<=_val) ++_notMore;
37 }
38 
39 inline void countBucket(int _bktIdx,int _val,int& _notMore)
40 {
41     using std::upper_bound;
42     
43     int* ub=upper_bound(bucket[_bktIdx],bucket[_bktIdx]+bktSize,_val);
44     _notMore+=(ub-bucket[_bktIdx]);
45 }
46 
47 int ask(int _rangeL,int _rangeR,int _k) //k-th smallest
48 {
49     int digitL=_rangeL>>bktDigit;
50     int digitR=_rangeR>>bktDigit;
51     int vL=0;
52     int vR=N-1;
53     
54     while(vL<vR)
55     {
56         int midV=(vL+vR)>>1;
57         int notMore=0;
58         if(digitL==digitR) 
59             enumerate(_rangeL,_rangeR,ordered[midV],notMore);
60         else
61         {
62             for(int i=digitL+1;i<digitR;i++)
63                 countBucket(i,ordered[midV],notMore);
64             enumerate(_rangeL,((digitL+1)<<bktDigit)-1,ordered[midV],notMore);
65             enumerate(digitR<<bktDigit,_rangeR,ordered[midV],notMore);
66         }
67         
68         if(notMore<_k) vL=midV+1;
69         else vR=midV;
70     }
71     return ordered[vL];
72 }
73 
74 int main()
75 {
76     init();
77     for(int i=0;i<K;i++)
78     {
79         int l,r,k;
80         scanf("%d%d%d",&l,&r,&k);
81         printf("%d\\n",ask(l-1,r-1,k));    
82     }
83     return 0;
84 }
View Code

 

1、为什么统计notMore,而不是统计less或者两者都统计?

二分的过程中,缩减区间的关键是:

1、必须使可能成为最终解的值保留在二分区间中

2、每一次都必须使区间大小的确被缩减,以防陷入死循环

在这道题中,某个值x为解的条件是:less(x)<x && notMore(x)>=x

如果统计Less的话,上面的代码很难是保证第一条的

而如果两者都统计的话,表面上当x满足条件时即可跳出,可以减少二分所需的时间

但是事实上,这样做的代价就是统计的时间复杂度常数乘以2,总的来说得不偿失(会TLE)

2、二分的对象是什么?可否把maxValue和minValue作为二分的对象?

Answer:NO!!!

正确的做法是将原数组排好序,然后对这个有序数组二分

理由很简单:范围小。二分区间长不会超过1e5

如果对数值本身二分的话,minValue和maxValue最坏时分别会达到-1e9和+1e9,二分的时间代价是前者的1.9倍

3、平方分割必须是严格的么?

Answer:NO(*^__^*)

设数据规模为N,每个桶的大小为B,则单次询问的时间复杂度为: O ( (N / B ) * log B + B )

当B = O ( ( N * log N ) ^ 0.5 ) 时,总的时间复杂度会比严格的平方分割小一些

代码中将B取为了1024正是为此。(顺便也方便了位运算)

B取512时效率会相对变差,B取256时干脆TLE

 

这道题更好的做法是归并树,比归并树还好的做法是划分树,不过这都是后话了,有时间慢慢填坑

以上是关于POJ2104 K-th Number 静态区间第k最值 平方分割的主要内容,如果未能解决你的问题,请参考以下文章

POJ2104 K-th Number(主席树)

[POJ2104]K-th Number(区间第k值 记录初始状态)

POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

POJ2104 K-th Number(线段树,二分,vector)

poj2104 K-th Number 主席树入门;

POJ-2104 K-th Number CDQ分治