题目大意:
就是给出一个静态的序列然后多次询问问某一连续的数中的第k大的数是多少, k也会变化
大致思路:
主席树学习第一题...
这个函数式线段树的思路感觉好巧妙= =
对于给出的序列离散化之后对于离散化之后的值域建线段树, 对于序列的每一个前缀都建立线段树, 然后充分利用以前的版本, 使得空间复杂度降到O(nlogn)的级别
由于支持访问历史版本, 可以利用减法来得到需要的区间[l, r]中的值在[L, R]中的数的个数, 于是查询操作就和线段树很相似了
代码如下:
Result : Accepted Memory : 25248 KB Time : 1688 ms
/*
* Author: Gatevin
* Created Time: 2015/9/9 19:59:48
* File Name: ChairTree.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 100010
namespace ChairTree
{
struct Node
{
int ls, rs, w;
Node(){ ls = rs = w = 0; }
};
Node T[maxn*20];
int a[maxn];//要处理的序列
int b[maxn];//离散化之后的a数组
int p[maxn];//p[i]在排序之后是第i个数再原序列a中的位置
int root[maxn];//root[i]是插入第i个数的时候第i棵线段树的根节点的编号, 那个结点是T[root[i]]
int sz;//结点总数
int cmp(int i, int j)
{
return a[i] < a[j];
}
int n, m;
void insert(int &i, int l, int r, int x)//插入数x
{
T[++sz] = T[i];
i = sz;
T[i].w++;//位于区间[l, r]中的数多了一个
if(l == r) return;
int mid = (l + r) >> 1;
if(x <= mid) insert(T[i].ls, l, mid, x);
else insert(T[i].rs, mid + 1, r, x);
}
int query(int i, int j, int l, int r, int k)
{
if(l == r) return l;
int cnt = T[T[j].ls].w - T[T[i].ls].w;//对于区间[l, r], 所询问的序列区间中在区间[l, mid]中的数的个数
int mid = (l + r) >> 1;
if(cnt >= k) return query(T[i].ls, T[j].ls, l, mid, k);//说明第k小在[l, mid]中, 于是两个版本的线段树都向左递归
else return query(T[i].rs, T[j].rs, mid + 1, r, k - cnt);//否则都向右递归, 查找右区间中的k - cnt小数
}
void solve()
{
while(scanf("%d %d", &n, &m) != EOF)
{
root[0] = 0;
sz = 0;//初始化主席树
for(int i = 1; i <= n; i++)
scanf("%d", a + i);
for(int i = 1; i <= n; i++) p[i] = i;
sort(p + 1, p + n + 1, cmp);//对p[i]排序a[p[i]] <= a[p[i + 1]]
for(int i = 1; i <= n; i++)
b[p[i]] = i;//此时b[i]相当于离散化之后的a数组, 并且离散成各不相同的1~n的排列
for(int i = 1; i <= n; i++)
{
root[i] = root[i - 1];//将根节点要复制的信息存下来
insert(root[i], 1, n, b[i]);
}
for(int i = 1; i <= m; i++)
{
int x, y, k;
scanf("%d %d %d", &x, &y, &k);
int ans = query(root[x - 1], root[y], 1, n, k);//这个第k大是原a序列中的第ans大的数
printf("%d\n", a[p[ans]]);
}
}
}
};
int main()
{
ChairTree::solve();
return 0;
}