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:\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 }

 

你可能感兴趣的:(count)