- 輸入
- 第一行有個整數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;
}