NYOJ 600 花兒朵朵

描述
輸入
第一行有個整數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
春天到了,花兒朵朵盛開,hrdv是一座大花園的主人,在他的花園裏種着許多種鮮花,每當這個時候,就會有一大羣遊客來他的花園欣賞漂亮的花朵,遊客們總是會詢問,某個時間有多少種花兒同時在盛開着?hrdv雖然知道每種花兒的開花時間段,但是他不能很快的答出遊客的問題,你能編寫一個程序幫助他嗎?


看到這道題很容易想到線段樹,但是會發現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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章