POJ 2104 K-th Number
题意:给出一个序列,若干查询,询问区间l到r的第k小值。
解法:划分树可以最有效的解决这个问题……但是划分树只能解决这个问题= =主席树的应用范围比较广……所以只学了主席树……嗯……
膜拜发明主席树的大神,引用一下他的话
..这个东西是当初我弱不会划分树的时候写出来替代的一个玩意..被一小撮别有用心的人取了很奇怪的名字> <
想法是对原序列的每一个前缀[1..i]建立出一颗线段树维护值域上每个数的出现次数,然后发现这样的树是可以减的,然后就没有然后了
通过这位大神的博客学习了原理http://blog.csdn.net/metalseed/article/details/8045038
通过这位大神的博客学习了代码http://www.cnblogs.com/Rlemon/archive/2013/05/23/3094635.html
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<math.h>
#include<limits.h>
#include<time.h>
#include<stdlib.h>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define LL long long
#define MAXN 100010
using namespace std;
struct node
{
int sum;
int ls, rs;
node() {sum = 0, ls = 0, rs = 0;}
}T[MAXN * 20];
int a[MAXN], b[MAXN], p[MAXN];//a是原数组,p代表a数组下标,b是离散化后数组
int root[MAXN];//记录每个结点在T中的下标
int sz;//当前数组使用长度
void ins(int &i, int l, int r, int x)
{
T[++sz] = T[i];//复制上一节点作为新结点
i = sz;//更新当前结点下标
T[i].sum++;//更新线段树维护的和
if(l == r)
return ;
int m = (l + r) >> 1;
if(x <= m) ins(T[i].ls, l, m, x);//更改左儿子
else ins(T[i].rs, m + 1, r, x);//更改右儿子
}
int query(int i, int j, int l, int r, int k)
{
if(l == r)
return l;
int t = T[T[j].ls].sum - T[T[i].ls].sum;//r线段树减去l-1的线段树值
int m = (l + r) >> 1;
if(t >= k) return query(T[i].ls, T[j].ls, l, m, k);
else return query(T[i].rs, T[j].rs, m + 1, r, k - t);
}
bool cmp(int i, int j)
{
return a[i] < a[j];
}
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), p[i] = i;
sort(p + 1, p + n + 1, cmp);//按原数组大小对下标排序
for(int i = 1; i <= n; i++)//离散化
b[p[i]] = i;
sz = 0;
root[0] = 0;
for(int i = 1; i <= n; i++)
{
root[i] = root[i - 1];
ins(root[i], 1, n, b[i]);
}
while(m--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int t = query(root[l - 1], root[r], 1, n, k);
printf("%d\n", a[p[t]]);
}
}
return 0;
}

浙公网安备 33010602011771号