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 }

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

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

时间: 2024-12-09 08:53:47

POJ2104 K-th Number 静态区间第k最值 平方分割的相关文章

HDU3473--Minimum Sum(静态区间第k大)

Minimum Sum Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3047    Accepted Submission(s): 701 Problem Description You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you

主席树(静态区间第k大)

前言 如果要求一些数中的第k大值,怎么做? 可以先就这些数离散化,用线段树记录每个数字出现了多少次. ... 那么考虑用类似的方法来求静态区间第k大. 原理 假设现在要有一些数 我们可以对于每个数都建一棵新的线段树,用来记录出现每个数字出现了多少次的前缀和. 那么假设要求区间[l,r]的第k大,将第r棵线段树减去第l-1棵线段树,像上面求所有数的第k大一样来求就可以了. 但是,对于每一个数都建一个线段树显然会爆空间. 现在考虑如何节约空间. 假设现在有四个数1 4 2 3,依次加入,可以这样处理

POJ2104-- K-th Number(主席树静态区间第k大)

[转载]一篇还算可以的文章,关于可持久化线段树http://finaltheory.info/?p=249 无修改的区间第K大 我们先考虑简化的问题:我们要询问整个区间内的第K大.这样我们对值域建线段树,每个节点记录这个区间所包含的元素个数,建树和查询时的区间范围用递归参数传递,然后用二叉查找树的询问方式即可:即如果左边元素个数sum>=K,递归查找左子树第K大,否则递归查找右子树第K – sum大,直到返回叶子的值. 现在我们要回答对于区间[l, r]的第K大询问.如果我们能够得到一个插入原序

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

题目链接:http://poj.org/problem?id=2104 给n个数和m个查询,查询[i, j]内第k小的数是多少.(主席树.划分树那种高大上的姿势叒不会啊QAQ 可以在维护这n个数的同时维护刚刚输入的时候他们的下标,之后预处理排序一次,查询的时候假如初始下标在[i,j]内,那么k自减1,直到k为0. 1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include &l

poj2761静态区间第k大

例题:poj2761 题目要求:给定一个长度为n的序列,给定m个询问,每次询问求[l,r]区间内的第k大: 对于这道题目来说,很多算法都可以使用,比如说树套树(一个负责划分区间,一个负责维护这段区间内的信息),主席树等: 对这道题我使用的是主席树: 主席树对付区间第k大是很优秀的,代码短,而且常数小: 主席树的主要功能是,建立n颗范围是1-i的权值线段树,对两颗线段树做差,就可以任意一个区间内的权值线段树: 详细看代码: #include<iostream> #include<cstdi

静态区间第k大 树套树解法

然而过不去你谷的模板 思路: 值域线段树\([l,r]\)代表一棵值域在\([l,r]\)范围内的点构成的一颗平衡树 平衡树的\(BST\)权值为点在序列中的位置 查询区间第\(k\)大值时 左区间在\([l,r]\)范围内的树的大小与\(k\)比较 大了进去,小了减掉换一边 关于建树 递归建估计是\(O(nlog^2n)\)的 Code: #include <cstdio> #include <cstdlib> #include <algorithm> #includ

[poj 2104]主席树+静态区间第k大

题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即可得到第k大(小)元素. #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define lson(i) node[i].lson #define rson(i) node

HDU3727--Jewel (主席树 静态区间第k大)

Jewel Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 985    Accepted Submission(s): 247 Problem Description Jimmy wants to make a special necklace for his girlfriend. He bought many beads with

主席树——求静态区间第k大

例题:poj2104 http://poj.org/problem?id=2104 讲解:http://blog.sina.com.cn/s/blog_6022c4720102w03t.html http://seter.is-programmer.com/posts/31907.html 刚刚根据以上2篇博客看懂,还没到写讲解的水平 #include<cstdio> #include<algorithm> using namespace std; const int N=1000