zoukankan      html  css  js  c++  java
  • UPC 2224 / “浪潮杯”山东省第四届ACM大学生程序设计竞赛 1008 Boring Counting 主席树

    Problem H:Boring Counting

    Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)

    Problem Description

    In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries, for each query, please tell us among [L, R], how many Pi is not less than A and not greater than B( L<= i <= R). In other words, your task is to count the number of Pi (L <= i <= R,  A <= Pi <= B).

    Input

    In the first line there is an integer T (1 < T <= 50), indicates the number of test cases.
            For each case, the first line contains two numbers N and M (1 <= N, M <= 50000), the size of sequence P, the number of queries. The second line contains N numbers Pi(1 <= Pi <= 10^9), the number sequence P. Then there are M lines, each line contains four number L, R, A, B(1 <= L, R <= n, 1 <= A, B <= 10^9)

    Output

    For each case, at first output a line ‘Case #c:’, c is the case number start from 1. Then for each query output a line contains the answer.

    Sample Input

    1

    13 5

    6 9 5 2 3 6 8 7 3 2 5 1 4

    1 13 1 10

    1 13 3 6

    3 6 3 6

    2 8 2 8

    1 9 1 9

    Sample Output

    Case #1:

    13

    7

    3

    6

    9

     

    给你n个数和m条询问,每次查询区间[l,r]中满足A<=x<=B的x的个数。

    感觉很不错的一道题,考察主席树的应用。

    关于主席树,请参考:http://seter.is-programmer.com/posts/31907.html

    建立n棵线段树,线段树root[i]代表区间[1,i]之间一段数字区间出现的次数。

    对于每一棵线段树,每个节点表示一个区间[a,b],记录满足a<=x<=b的x的个数。

    因为线段树root[i]是在线段树root[i-1]的基础上增加了一个数得到的,所以可以由root[i-1]得到root[i]。

    由于每棵线段树的大小形态都是一样的,而且初始值全都是0,那每个线段树都初始化不是太浪费了?所以一开始只要建一棵空树即可。

    然后是在某棵树上修改一个数字,由于和其他树相关联,所以不能在原来的树上改,必须弄个新的出来。难道要弄一棵新树?不是的,由于一个数字的更改只影响了一条从这个叶子节点到根的路径,所以只要只有这条路径是新的,另外都没有改变。比如对于某个节点,要往右边走,那么左边那些就不用新建,只要用个指针链到原树的此节点左边就可以了,这个步骤的前提也是线段树的形态一样。

    n是数字个数,这个步骤的空间复杂度显然是O(logn)。

    所有树节点的空间消耗为:O(n*4+nlogn)

    预处理得到所有的root[i]。

    查询时在两棵线段树root[R]和root[L-1]中分别查询区间[A,B]中数的个数,相减即是结果。

    本题的时间复杂度:建树:O(n)+预处理O(nlogn)+查询O(logn)

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cstdlib>
      4 #include <algorithm>
      5 
      6 using namespace std;
      7 
      8 #define lson l, m, rt->left
      9 #define rson m + 1, r, rt->right
     10 
     11 const int MAXN = 52222;
     12 const int INF = (1e9)+10;
     13 
     14 struct Node
     15 {
     16     int sum;   //存储区间[A, B]之间一共有多少个数
     17     Node *left, *right;
     18 };
     19 
     20 Node *root[MAXN];
     21 Node ChairTree[ MAXN * 40 ];
     22 Node *idx;
     23 
     24 int pos[MAXN];  //处于位置i的数排第几
     25 int num[MAXN];  //排好序并去重的原始数,相当于把所有数离散化之后的结果
     26 int p[MAXN];    //排第i的是哪个数
     27 int cnt, n, Q;
     28 
     29 bool cmp( int a, int b )
     30 {
     31     return pos[a] < pos[b];
     32 }
     33 
     34 Node *nextNode()
     35 {
     36     idx->sum = 0;
     37     idx->left = idx->right = NULL;
     38     return idx++;
     39 }
     40 
     41 Node *copyNode( Node *ori )
     42 {
     43     idx->sum = ori->sum;
     44     idx->left = ori->left;
     45     idx->right = ori->right;
     46     return idx++;
     47 }
     48 
     49 void PushUp( Node *rt )
     50 {
     51     rt->sum = rt->left->sum + rt->right->sum;
     52     return;
     53 }
     54 
     55 void build( int l, int r, Node* rt )   //建立一个空树
     56 {
     57     if ( l == r ) return;
     58 
     59     int m = ( l + r ) >> 1;
     60 
     61     rt->left = nextNode();
     62     rt->right = nextNode();
     63 
     64     build( lson );
     65     build( rson );
     66 
     67     return;
     68 }
     69 
     70 void init()
     71 {
     72     scanf( "%d%d", &n, &Q );
     73 
     74     for ( int i = 1; i <= n; ++i )
     75     {
     76         scanf( "%d", &pos[i] );
     77         p[i] = i;
     78     }
     79     sort( p + 1, p + 1 + n, cmp );
     80 
     81     int pre = -INF;
     82     cnt = 0;
     83     for ( int i = 1; i <= n; ++i )  //离散化+去重
     84     {
     85         if ( pos[ p[i] ] == pre )
     86             pos[ p[i] ] = cnt;
     87         else
     88         {
     89             pre = pos[ p[i] ];
     90             num[ ++cnt ] = pos[ p[i] ];
     91             pos[ p[i] ] = cnt;
     92         }
     93     }
     94     return;
     95 }
     96 
     97 Node *add( int val, int l, int r, Node* rt )  //根据上一棵树构造下一棵树
     98 {
     99     Node *temp = copyNode( rt );
    100 
    101     if ( l == r )
    102     {
    103         temp->sum += 1;
    104         return temp;
    105     }
    106     int m = ( l + r ) >> 1;
    107     if ( val <= m ) temp->left = add( val, lson );
    108     else temp->right = add( val, rson );
    109     PushUp( temp );
    110 
    111     return temp;
    112 }
    113 
    114 int Query( int L, int R, int l, int r, Node* treeL, Node* treeR )
    115 {
    116     if ( L <= l && r <= R )
    117     {
    118         return treeR->sum - treeL->sum;
    119     }
    120     int res = 0;
    121 
    122     int m = ( l + r ) >> 1;
    123     if ( L <= m ) res += Query( L, R, l, m, treeL->left, treeR->left );
    124     if ( R > m ) res += Query( L, R, m + 1, r, treeL->right, treeR->right );
    125     return res;
    126 }
    127 
    128 void chuli()
    129 {
    130     idx = ChairTree;
    131     root[0] = nextNode();
    132     build( 1, cnt, root[0] );   //建立一颗空树
    133 
    134     for ( int i = 1; i <= n; ++i )
    135         root[i] = add( pos[i], 1, cnt, root[i - 1] );   //根据第i-1棵树构造第i棵树
    136     return;
    137 }
    138 
    139 int BiSearch1( int tar )   //查询最左边的 >= x 的数
    140 {
    141     int low = 1, high = cnt;
    142     int mid, ans = -1;
    143     while ( low <= high )
    144     {
    145         mid = ( low + high ) >> 1;
    146         if ( num[mid] >= tar )
    147         {
    148             ans = mid;
    149             high = mid - 1;
    150         }
    151         else low = mid + 1;
    152     }
    153     return ans;
    154 }
    155 
    156 int BiSearch2( int tar )   //查询最右边的 <= x 的数
    157 {
    158     int low = 1, high = cnt;
    159     int mid, ans = -1;
    160     while ( low <= high )
    161     {
    162         mid = ( low + high ) >> 1;
    163         if ( num[mid] <= tar )
    164         {
    165             ans = mid;
    166             low = mid + 1;
    167         }
    168         else high = mid - 1;
    169     }
    170     return ans;
    171 }
    172 
    173 int main()
    174 {
    175     int T, cas = 0;
    176     scanf( "%d", &T );
    177     while ( T-- )
    178     {
    179         init();
    180         chuli();
    181 
    182         printf( "Case #%d:
    ", ++cas );
    183         while ( Q-- )
    184         {
    185             int l, r, a, b;
    186             scanf("%d%d%d%d", &l, &r, &a, &b );
    187             int u = BiSearch1( a );
    188             int v = BiSearch2( b );
    189             if ( u == -1 || v == -1 )
    190             {
    191                 puts("0");
    192                 continue;
    193             }
    194             printf( "%d
    ", Query( u, v, 1, cnt, root[l - 1], root[r] ) );
    195         }
    196     }
    197     return 0;
    198 }
  • 相关阅读:
    一些好用的小工具
    App随机测试之Monkey和Maxim
    Appium如何自动判断浏览器驱动
    最简单的一个Appium测试Android Web APP的代码demo
    pytest使用allure生成测试报告的2种命令
    使用order by in()将快到期的数据排到最上方。
    关于jQuery click()方法重复提交的问题
    关于List removeAll失效的问题
    根据年和月计算对应的天数
    jquery通过监听输入框实现值的自动计算
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3147456.html
Copyright © 2011-2022 走看看