题目大意:
就是给出一个静态的序列然后多次询问问某一连续的数中的第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; }