简述:
解决线段树无法求区间第k大的问题
代码:

1 ///主席树模版(查询区间第k大)
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cstring>
5 #include <algorithm>
6 #include <cctype>
7 #define numm ch-48
8 #define pn putchar('\n')
9 #define pd putchar(' ')
10 //#define debug(args...) cout<<#args<<"->"<<args<<endl
11 //#define bug cout<<"*******************"<<endl
12 using namespace std;
13 template<typename T>
14 void read(T &res) {
15 char ch;bool flag=false;
16 while(!isdigit(ch=getchar()))(ch=='-')&&(flag=true);
17 for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
18 flag&&(res=-res);
19 }
20 template<typename T>
21 void write(T x) {
22 if(x<0) x=-x,putchar('-');
23 if(x>9) write(x/10);
24 putchar(x%10+48);
25 }
26 typedef long long ll;
27 const int maxn=100010+10;
28 const int inf=0x3f3f3f3f;
29 struct bb {
30 int x,pos;
31 bool operator<(const bb& c) {
32 return x==c.x?pos<c.pos:x<c.x;
33 }
34 }b[maxn];
35 struct node {
36 int l,r; ///当前结点的管辖范围
37 int zuo,you;///当前结点的左右儿子标号
38 int c; ///记录存在于这个区间的点的个数
39 }tree[maxn*40];///一般乘上nlogn就够用
40 int id,a[maxn],gen[maxn],first[maxn];
41 ///id:每个树结点的标号
42 ///a[]:去重后且离散化后的数组
43 ///gen[]:树根结点的标号
44 void build(int l,int r) {
45 id++;
46 tree[id].c=0;
47 tree[id].l=l;
48 tree[id].r=r;
49 tree[id].zuo=tree[id].you=-1;
50 int now=id; ///attention!!!
51 if(l<r) {
52 int mid=l+r>>1;
53 tree[now].zuo=id+1;build(l,mid); ///now不要写成id
54 tree[now].you=id+1;build(mid+1,r);
55 }
56 }
57 void add(int x) {
58 int now=gen[x-1];gen[x]=id+1;
59 do {
60 int zuo=tree[now].zuo,you=tree[now].you;
61 ///zuo:now的左儿子标号,you同理
62 id++;
63 tree[id].c=tree[now].c+1;///插入一个数,比上一次多1
64 tree[id].l=tree[now].l;
65 tree[id].r=tree[now].r;
66 if(zuo==-1) {///当前是叶子结点
67 tree[id].zuo=tree[id].you=-1;
68 break;
69 }
70 if(a[x]<=tree[zuo].r) { ///大小在左子树范围内
71 tree[id].zuo=id+1;
72 tree[id].you=you;///沿用上一棵树的右子树
73 now=zuo;///递归左子树
74 }
75 else { ///同理
76 tree[id].zuo=zuo;///沿用上一棵树的左子树
77 tree[id].you=id+1;
78 now=you;///递归右子树
79 }
80 }while(now!=-1);
81 }
82 int found(int x,int y,int k) {
83 int l=gen[x-1],r=gen[y];
84 while(tree[r].zuo!=-1) {///一直判到叶子结点
85 int zuo_sum=tree[tree[r].zuo].c-tree[tree[l].zuo].c;
86 int you_sum=tree[tree[r].you].c-tree[tree[l].you].c;
87 ///统计左右结点的数的个数
88 if(k<=zuo_sum) {///如果在左儿子里,就往左儿子走
89 l=tree[l].zuo;
90 r=tree[r].zuo;
91 }
92 else { ///如果在右儿子里,就往右儿子走
93 l=tree[l].you;
94 r=tree[r].you;
95 k-=zuo_sum; ///attention!!!
96 }
97 }
98 return tree[r].l;
99 }
100 int main()
101 {
102 int n,m;
103 read(n),read(m);///n个树,m个询问
104 for(int i=1;i<=n;i++) ///读入n个数
105 read(b[i].x),b[i].pos=i;
106 sort(b+1,b+1+n);
107 int cnt=0;
108 for(int i=1;i<=n;i++) { ///下面进行去重
109 if(i==1||b[i].x!=b[i-1].x) a[b[i].pos]=++cnt;///直接在a数组上修改
110 else a[b[i].pos]=cnt;
111 first[cnt]=b[i].x; ///记录这个序号对应的x
112 }
113 build(1,n); ///建树
114 gen[0]=1; ///由上一步得知,第一棵树的根标号是1
115 for(int i=1;i<=n;i++)///插入结点(建立剩下的n棵树)
116 add(i);
117 for(int i=1;i<=m;i++) {
118 int l,r,k;
119 read(l);read(r);read(k);
120 write(first[found(l,r,k)]);pn;
121 }
122 return 0;
123 }
