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:\n", ++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\n", Query( u, v, 1, cnt, root[l - 1], root[r] ) ); 195 } 196 } 197 return 0; 198 }