zoukankan      html  css  js  c++  java
  • SPOJ--K-query (线段树离线) 离线操作解决一些问题

                                  K-query

    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

    • Line 1: n (1 ≤ n ≤ 30000).
    • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 109).
    • Line 3: q (1 ≤ q ≤ 200000), the number of k- queries.
    • In the next q lines, each line contains 3 numbers i, j, k representing a k-query (1 ≤ i ≤ j ≤ n, 1 ≤ k ≤ 109).

    Output

    • For each k-query (i, j, k), print the number of elements greater than k in the subsequence ai, ai+1, ..., aj in a single line.

    Example

    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
    ", 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的值。然后两者比较即可。 代码略。

  • 相关阅读:
    SharePoint Development
    win32- copyfile的使用
    SetWindowHookEx的复习
    C++ vector的使用
    关于char * 和 char [] 的一点理解
    CreateThread
    C++字符串大写字母转小写字母
    字符串逆序
    使用RegSetValueEx创建键值
    EnumColorProfiles WcsGetDefaultColorProfile WcsSetDefaultColorProfile的使用
  • 原文地址:https://www.cnblogs.com/oneshot/p/4706043.html
Copyright © 2011-2022 走看看