- 输入
- 第一行有个整数t,表示有t组测试数据,每组测试数据第一行为两个整数n,m(0<n<100000,0<m<100000);随后有n行,每一行有两个整数x,y(0<x<y<1000000000),表示这一种花的盛开时间是从x到y;随后有m行,每行有一个整数,代表游客询问的时间。
- 输出
- 对于每次游客的询问,输出一个整数在单独的一行,表示这个时间盛开的花有多少种。
- 样例输入
-
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;
}