对应题目链接:点击打开链接
Time Limit: 10000MS | Memory Limit: 32768KB | 64bit IO Format: %lld & %llu |
Description
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.
Your task is to write a program for this computer, which
- Reads N numbers from the input (1 <= N <= 50,000)
- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.
Input
Output
Sample Input
Sample Output
题意:
n 个数,m 个询问。 Q(l, r, k) 表示询问区间 [l, r] 内第 k 小的数,C(pos, val) 表示把下标为 pos 的数的值该为 val
思路:
树套树入门题,这里选择线段树套Treap树实现。
假设 n 个数用数组 a[] 表示。
线段树可用于区间查询,而平衡树可用于查询区间内某个数的名次;因此可以为线段树的每个区间 [l, r] 都建立一颗以 a[l], a[l+1], ... , a[r] 为键值的平衡树
之后就可以很容易用递归的方法查询 x 在区间 [l, r] 的排名,只需要实现一个 Query(l, r, x),它返回的是区间 [l, r] 中比 x 小的数的个数,这样 Query(l, r, x) + 1 就是 x 在区间 [l, r] 内的排名;然而题目要求的是求区间 [l, r] 内排名为 k 的数,这怎么办?我们可以用二分答案的办法,具体的过程是:
先找到区间 [l, r] 内的最小值 min 和最大值 max,以此作为二分的两端 beg 和 end(如果嫌麻烦也可以直接使用 a[] 或者取值范围里的最大值最小值);令 mid_val = (beg + end) / 2,用 Query(l, r, mid_val) 计算 mid_val 的排名,如果 mid_val 排名不大于 k ,则在右半边找,否则在左半边找;二分这里需要注意一些边界问题。
用代码描述就是:
while(beg < end){ mid_val = beg + (end - beg) / 2; num = Query(l, r, mid_val); if(num + 1 <= k){ ans = mid_val; beg = mid + 1; } else end = mid; } print(ans);
比如要修改 pos = 3 这个位置的值,则需要把图中红色区间对应的每一颗平衡树进行修改。
这样,线段树区间查找的复杂度为 O(logn),平衡树查找复杂度为 O(logn),二分查找的复杂度为 O(logn),则总时间复杂度为 O((logn)^3),而空间复杂度基本为 lay * n ,lay = logn(即线段树的深度);此题我使用指针动态分配内存会导致MLE,所以最好使用静态数组。
最后~这道题的 N 要开大一点点。。。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #define N 70010 int a[N]; int root[N<<2]; int node_count; typedef struct { int val; int rank; int sz; int ch[2]; }Node; Node node[16*N]; void init() { node_count = 0; memset(root, 0, sizeof(root)); node[0].val = node[0].rank = node[0].sz = node[0].ch[0] = node[0].ch[1] = 0; } void push_up(int rt) { if(!rt) return; node[rt].sz = node[node[rt].ch[0]].sz + node[node[rt].ch[1]].sz + 1; } void rotate(int *x, int flag) { int y = node[*x].ch[flag]; node[*x].ch[flag] = node[y].ch[flag^1]; node[y].ch[flag^1] = *x; push_up(*x); (*x) = y; } void insert(int *rt, int val) { if(!(*rt)){ (*rt) = ++node_count; node[*rt].val = val; node[*rt].sz = 1; node[*rt].rank = rand(); node[*rt].ch[0] = node[*rt].ch[1] = 0; } else{ int flag = val > node[*rt].val; insert(&node[*rt].ch[flag], val); if(node[node[*rt].ch[flag]].rank < node[*rt].rank) rotate(rt, flag); } push_up(*rt); } void build_bst(int *rt, int l, int r) { int i; for(i = l; i <= r; i++) insert(rt, a[i]); } void build(int rt, int left, int right) { int mid; build_bst(&root[rt], left, right); if(left == right) return; mid = left + (right - left) / 2; build(rt<<1, left, mid); build(rt<<1|1, mid + 1, right); } int small_then_val(int rt, int val) { int p = rt; int ans = 0; while(p){ if(val > node[p].val){ ans += node[node[p].ch[0]].sz + 1; p = node[p].ch[1]; } else p = node[p].ch[0]; } return ans; } void delete(int *rt, int val) { if(!(*rt)) return; else if(val == node[*rt].val){ if(!node[*rt].ch[0] && !node[*rt].ch[1]) *rt = 0; else if(!node[*rt].ch[1]) *rt = node[*rt].ch[0]; else if(!node[*rt].ch[0]) *rt = node[*rt].ch[1]; else{ int flag = node[node[*rt].ch[0]].rank > node[node[*rt].ch[1]].rank; rotate(rt, flag); delete(&node[*rt].ch[flag^1], val); } } else if(val > node[*rt].val) delete(&node[*rt].ch[1], val); else delete(&node[*rt].ch[0], val); push_up(*rt); } void update(int rt, int left, int right, int pos, int val) { int mid; delete(&root[rt], a[pos]); insert(&root[rt], val); if(left == right) return; mid = left + (right - left) / 2; if(pos > mid) update(rt<<1|1, mid + 1, right, pos, val); else update(rt<<1, left, mid, pos, val); } int Query(int rt, int left, int right, int l, int r, int val) { int mid; if(left == l && right == r) return small_then_val(root[rt], val); mid = left + (right - left) / 2; if(l > mid) return Query(rt<<1|1, mid + 1, right, l, r, val); else if(r <= mid) return Query(rt<<1, left, mid, l, r, val); else return Query(rt<<1, left, mid, l, mid, val) + Query(rt<<1|1, mid + 1, right, mid + 1, r, val); } int main() { #if 0 freopen("in.txt","r",stdin); #endif int T; srand((unsigned int)time(NULL)); scanf("%d", &T); while(T--){ int i; int n, m; init(); scanf("%d%d", &n, &m); for(i = 0; i < n; i++) scanf("%d", a + i); build(1, 0, n - 1); for(i = 0; i < m; i++){ int l, r, k, pos, val; int beg = 0; int end = 1000000000; int num, mid, ans; char od[5]; scanf("%s", od); if(od[0] == 'Q'){ scanf("%d%d%d", &l, &r, &k); l--; r--; while(beg < end){ mid = beg + (end - beg) / 2; num = Query(1, 0, n - 1, l, r, mid); if(num + 1 <= k){ ans = mid; beg = mid + 1; } else end = mid; } printf("%d\n", ans); } else{ scanf("%d%d", &pos, &val); pos--; update(1, 0, n - 1, pos, val); a[pos] = val; } } } return 0; }