2 1 1 5 10 4 2 3 1 4 4 8 1 4 6
0 1 2 1
看到这道题很容易想到线段树,但是会发现x,y很大,而n和m比较小,所以可以
将所有的x,y和需要查询的点q全部保存进行离散化,然后用线段树或树状数组更新
和查询,需要注意细节。
#include <stdio.h> #include <algorithm> #include <string.h> #define lson l, m, rt << 1 #define rson m+1, r, rt << 1 | 1 #define INF 0x7fffffff const int maxn = 100005; int x[maxn], y[maxn]; int a[maxn << 5], tag[maxn << 5]; int arr[maxn << 2], q[maxn]; int binary_search ( int a[], int k, int l, int r ) { int m; while ( l <= r ) //重定义坐标 { m = ( l+r ) >> 1; if ( a[m] == k ) return m; if ( a[m] > k ) r = m-1; else l = m+1; } return -1; } void PushDown ( int rt, int m ) { if ( tag[rt] ) { tag[rt << 1] += tag[rt]; tag[rt << 1 | 1] += tag[rt]; a[rt << 1] += ( m-( m >> 1 ) )*tag[rt]; //注意乘tag[rt]个,一直错在这里了 a[rt << 1 | 1] += ( m >> 1 )*tag[rt]; tag[rt] = 0; } } void PushUP ( int rt ) { a[rt] = a[rt << 1]+a[rt << 1 | 1]; } void update ( int L, int R, int v, int l, int r, int rt ) { if ( L <= l && r <= R ) { tag[rt] += v; a[rt] += ( r-l+1 ); return ; } PushDown ( rt, r-l+1 ); int m = ( l+r ) >> 1; if ( L <= m ) update ( L, R, v, lson ); if ( R > m ) update ( L, R, v, rson ); PushUP ( rt ); } int query ( int q, int l, int r, int rt ) { if ( l == r ) return a[rt]; PushDown ( rt, r-l+1 ); int m = ( l+r ) >> 1; if ( q <= m ) return query ( q, lson ); else return query ( q, rson ); } void print ( int a[], int n ) { for ( int i = 1; i < n; i ++ ) printf ( "%d ", a[i] ); printf ( "\n" ); } int main ( ) { int T, n, m; scanf ( "%d", &T ); while ( T -- ) { memset ( a, 0, sizeof ( a ) ); memset ( tag, 0, sizeof ( tag ) ); scanf ( "%d%d", &n, &m ); int pos = 1; //将所有出现的坐标离散化 for ( int i = 0; i < n; i ++ ) { scanf ( "%d%d", &x[i], &y[i] ); arr[pos ++] = x[i]; arr[pos ++] = y[i]; } for ( int i = 0; i < m; i ++ ) { scanf ( "%d", &q[i] ); arr[pos ++] = q[i]; } std :: sort ( arr+1, arr+pos ); //print ( arr, pos ); int k = 2; for ( int i = 2; i < pos; i ++ ) if ( arr[i] != arr[i-1] ) arr[k ++] = arr[i]; //去重 for ( int i = 0; i < n; i ++ ) { int l = binary_search ( arr, x[i], 1, k-1 ); int r = binary_search ( arr, y[i], 1, k-1 ); //printf ( "lr %d %d\n", l, r ); update ( l, r, 1, 1, k-1, 1 ); } for ( int i = 0; i < m; i ++ ) { int qry = binary_search ( arr, q[i], 1, k-1 ); printf ( "%d\n", query ( qry, 1, k-1, 1 ) ); } } return 0; }
假设x的映射为l,y的映射为r,
那么树状数组更新add(l,1)
add ( r+1, -1 )
这样查询直接sum(qry)查询就好,
因为l和r+1更新的时候区间的每一个点最多会加1,
然后超过的(即r+1)会更新成-1,公共的根节点
抵消为0,注意这不是区间更新(此方法挺好的,我也只能理解)。
#include <stdio.h> #include <algorithm> #include <string.h> const int maxn = 100005; int x[maxn], y[maxn], k; int a[maxn << 2], s[maxn << 2], q[maxn]; int Bin ( int key, int l, int r ) { int m; while ( l <= r ) { m = ( l+r ) >> 1; if ( a[m] == key ) return m; if ( a[m] > key ) r = m-1; else l = m+1; } return -1; } inline int lowbit ( int x ) { return x & -x; } void add ( int x, int v ) { while ( x < k ) { s[x] += v; x += lowbit ( x ); } } int sum ( int x ) { int ret = 0; while ( x > 0 ) { ret += s[x]; x -= lowbit ( x ); } return ret; } void print ( int n ) { for ( int i = 1; i < n; i ++ ) printf ( "%d ", a[i] ); printf ( "\n" ); } int main ( ) { int T, n, m; scanf ( "%d", &T ); while ( T -- ) { memset ( s, 0, sizeof ( s ) ); scanf ( "%d%d", &n, &m ); int pos = 1; for ( int i = 0; i < n; i ++ ) { scanf ( "%d%d", &x[i], &y[i] ); a[pos ++] = x[i]; a[pos ++] = y[i]; } for ( int i = 0; i < m; i ++ ) { scanf ( "%d", &q[i] ); a[pos ++] = q[i]; } std :: sort ( a+1, a+pos ); k = 2; for ( int i = 2; i < pos; i ++ ) if ( a[i] != a[i-1] ) a[k ++] = a[i]; //离散化 去重 //print ( k ); for ( int i = 0; i < n; i ++ ) { int l = Bin ( x[i], 1, k-1 ); int r = Bin ( y[i], 1, k-1 ); add ( l, 1 ); add ( r+1, -1 ); //实际上是单点更新,l和r+1的公共父节点值会抵消 //这样保证查询l-r区间的任意一点的值都只会得到1 } for ( int i = 0; i < m; i ++ ) { int qry = Bin ( q[i], 1, k-1 ); printf ( "%d\n", sum ( qry ) ); //查询前缀和就行,不在区间值就会相互抵消 } } return 0; }