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;
}


你可能感兴趣的:(NYOJ 600 花儿朵朵)