标签:
Given a sequence of n numbers a1, a2, ..., an and a number of k- queries. A k-query is a triple (i, j, k) (1 ≤ i ≤ j ≤ n). For each k-query (i, j, k), you have to return the number of elements greater than k in the subsequence ai, ai+1, ..., aj.
Input 5 5 1 2 3 4 3 2 4 1 4 4 4 1 5 2 Output 2 0 3
题意:给定 一个数组, q次询问, 每次询问区间[L, R]内大于k的数字有多少个。
这题算是比较老比较水的题目了, 但是感觉 离线可以处理 很多 大量询问的问题。 其中思想很值得挖掘学习。
比如这题,先考虑线段树,这棵线段树的叶子都是1, 先把 所有询问按k的大小从小到大排序, 然后对于 每个询问可以把 小于当前询问k的数字的位置在线段树里置0,那么结果就是线段树的区间 求和问题来了。 这样Q次询问, 最终所有数字都加进来了 复杂度 并不高 O( N + Q * log N)
2 const int MAXN = 3e4+10; 3 const int MAXQ = 2e5+10; 4 struct Node{ 5 int L, R, k, idx; 6 Node (int L = 0, int R = 0, int k = 0, int idx = 0): 7 L(L), R(R), k(k), idx(idx){} 8 bool operator < (const Node &rhs)const{ 9 return k < rhs.k; 10 } 11 }Q[MAXQ]; 12 int sum[MAXN << 2]; 13 void build (int l, int r, int pos){ 14 if (l == r){ 15 sum[pos] = 1; 16 return; 17 } 18 int mid = (l + r) >> 1; 19 build(l, mid, pos<<1); 20 build(mid+1, r, pos<<1|1); 21 sum[pos] = sum[pos<<1] + sum[pos<<1|1]; 22 } 23 void update (int l, int r, int pos, int x, int val){ 24 if (l == r){ 25 sum[pos] = val; 26 return; 27 } 28 int mid = (l + r) >> 1; 29 if (x <= mid){ 30 update(l, mid, pos<<1, x, val); 31 }else{ 32 update(mid+1, r, pos<<1|1, x, val); 33 } 34 sum[pos] = sum[pos<<1] + sum[pos<<1|1]; 35 } 36 int query (int l, int r, int pos, int ua, int ub){ 37 if (ua <= l && ub >= r){ 38 return sum[pos]; 39 } 40 int mid = (l + r) >> 1; 41 int res = 0; 42 if (ua <mid){ 43 res += query(l, mid, pos<<1, ua, ub); 44 } 45 if (ub > mid){ 46 res += query(mid+1, r, pos<<1|1, ua, ub); 47 } 48 return res; 49 } 50 int ans[MAXQ], IDX[MAXN], val[MAXN]; 51 bool cmp(int i, int j){ 52 return val[i] < val[j]; 53 } 54 int main() {57 int n, q; 58 while (~ scanf ("%d", &n)){ 59 for (int i = 0; i < n; i++){ 60 scanf ("%d", val+i); 61 IDX[i] = i; 62 } 63 sort (IDX, IDX+n, cmp); 64 scanf ("%d", &q); 65 for (int i = 0; i < q; i++){ 66 int ua, ub, k; 67 scanf ("%d%d%d", &ua, &ub, &k); 68 Q[i] = Node(ua, ub, k, i); 69 } 70 sort (Q, Q+q); 71 build(1, n, 1); 72 int p = 0; 73 for (int i = 0; i < q; i++){ 74 while (p < n && val[IDX[p]] <= Q[i].k){ 75 update(1, n, 1, IDX[p]+1, 0); 76 p++; 77 } 78 ans[Q[i].idx] = query(1, n, 1, Q[i].L, Q[i].R); 79 } 80 for (int i = 0; i < q; i++){ 81 printf("%d\n", ans[i]); 82 } 83 } 84 return 0; 85 }
再来看15年编程之美复赛的一道题。
你正在和小冰玩一个猜数字的游戏。小冰首先生成一个长为N的整数序列A1, A2, …, AN。在每一轮游戏中,小冰会给出一个区间范围[L, R],然后你要猜一个数K。如果K在AL, AL+1, …, AR中,那么你获胜。
在尝试了几轮之后,你发现这个游戏太难(无聊)了。小冰决定给你一些提示,你每猜一次,小冰会告诉你K与AL, AL+1, …, AR中最接近的数的绝对差值,即min(|Ai - K|), L ≤ i ≤ R。
也是一个数组,多次询问,每次求区间内与k最接近的数字。
这题 可以可持久化线段树做, 但是代码量大, 除此之外还可以 离线+线段树, 最接近k的值 要么比k小 要么比k大。
先考虑比k小的时候, 首先 把 所有数字和询问放到一起排序,按值排序, 把询问和数字 分开, 然后 从小到大加入到线段树, 那么对于某次询问[L, R]此时线段树的区间[L, R]最大值就是比k小的最接近的k的值, 同理可以求出比k大的最接近k的值。然后两者比较即可。 代码略。
SPOJ--K-query (线段树离线) 离线操作解决一下问题
标签:
原文地址:http://www.cnblogs.com/oneshot/p/4706043.html