HDU 4614 (13年多校第二场1004)裸线段树

题意:给你N个花瓶,编号是0  到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花。

然后有2个操作。

操作1,a b c ,往在a位置后面(包括a)插b朵花,输出插入的首位置和末位置。

操作2,a b ,输出区间[a , b ]范围内的花的数量,然后全部清空。


很显然这是一道线段树。区间更新,区间求和,这些基本的操作线段树都可以logN的时间范围内完成。

操作2,很显然就是线段树的区间求和,求出[a , b]范围内的花朵的数量,区间更新,将整个区间全部变成0。

操作1,这里我们首先需要找出他的首位置和末位置,所以需要二分他的位置。

首先我们二分他的首位置, l = a , r = n ,在这个区间内二分,找出第一个0的位置,那就是该操作的首位置pos1。

然后再二分他的末位置,l = pos1 , r = n ,找到第b个0,就是该操作的末位置pos2,然后区间更新[pos1 ,pos2]全部置为1。


就像解题报告上讲的一样,这是一道很裸的线段树。

 

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <string>

#include <cmath>

#include <cstring>

#include <queue>

#include <set>

#include <vector>

#include <stack>

#include <map>

#include <iomanip>

#define PI acos(-1.0)

#define Max 2505

#define inf 1<<28

#define LL(x) ( x << 1 )

#define RR(x) ( x << 1 | 1 )

#define REP(i,s,t) for( int i = ( s ) ; i <= ( t ) ; ++ i )

#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

#define mp(a,b) make_pair(a,b)

#define PII pair<int,int>

using namespace std;

#define M 150005



inline void RD(int &ret) {

    char c;

    do {

        c = getchar();

    } while(c < '0' || c > '9') ;

    ret = c - '0';

    while((c=getchar()) >= '0' && c <= '9')

        ret = ret * 10 + ( c - '0' );

}

int n , m ;

int L[M] , R[M] , sum[M] ,add[M] ;



void init(){

    mem(sum ,0) ;

    mem(add, 0) ;

}

void push_up(int x){

    sum[x] = sum[LL(x)] + sum[RR(x)] ;

}

void push_down(int x){

    if(L[x] == R[x])return ;

    if(add[x] == 1){//全部置为1

        sum[x] = R[x] - L[x] + 1 ;

        sum[LL(x)] = R[LL(x)] - L[LL(x)] + 1 ;

        sum[RR(x)] = R[RR(x)] - L[RR(x)] + 1 ;

        add[LL(x)] = add[x] ;

        add[RR(x)] = add[x] ;

        add[x] = 0 ;

    }

    else if(add[x] == 2){//全部置为0

        sum[x] = 0 ;

        sum[LL(x)] = 0 ;

        sum[RR(x)] = 0 ;

        add[LL(x)] = add[x] ;

        add[RR(x)] = add[x] ;

        add[x] = 0 ;

    }

}

void build(int l , int r ,int u){

    L[u] = l ;

    R[u] = r ;

    sum[u] = 0 ;

    add[u] = 0 ;

    if(l == r)return ;

    int mid = l + r >> 1 ;

    build(l , mid ,LL(u)) ;

    build(mid + 1 ,r ,RR(u)) ;

}



void update(int l ,int r ,int u ,int op){

    if(l > R[u] || r < L[u])return ;

    push_down(u) ;

    if(l == L[u] && r == R[u]) {

        if(op == 1)

            sum[u] = R[u] - L[u] + 1 ;

        else sum[u] = 0 ;

        add[u] = op ;

        return ;

    }

    int mid = L[u] + R[u] >> 1 ;

    if(r <= mid){

        update(l ,r ,LL(u) , op) ;

    }

    else if(l > mid){

        update(l , r , RR(u),op) ;

    }

    else {

        update(l , mid ,LL(u),op) ;

        update(mid + 1  , r , RR(u) ,op) ;

    }

    push_up(u) ;

}

int query(int l ,int r ,int u){

    if(l > R[u] || r < L[u])return 0 ;

    push_down(u) ;

    if(l == L[u] && r == R[u]) {

        return sum[u] ;

    }

    int mid = L[u] + R[u] >> 1 ;

    if(r <= mid){

        return query(l , r, LL(u)) ;

    }

    else if(l > mid){

        return query(l , r ,RR(u)) ;

    }

    else {

        return query(l , mid , LL(u)) + query(mid + 1 , r , RR(u)) ;

    }

}

void Noanswer(){

    puts("Can not put any one.") ;

}

void answer(int p1, int p2){

    printf("%d %d\n",p1, p2) ;

}

void answer(int p){

    printf("%d\n",p) ;

}

void debug(int u){



    printf(" 节点 %d 区间 : %d - %d \n" , u ,L[u] ,R[u]) ;

    printf(" 左子树 %d 右子树 %d \n" , LL(u) ,RR(u) ) ;

    printf("父节点sum值:%d\n",sum[u]) ;

    push_down(u) ;

    if(L[u] == R[u])return ;

    debug(LL(u)) ;

    debug(RR(u)) ;

}

void solve1(int a , int b){

    int pos1 = inf ;

    int l = a , r = n ;

    int nn = n - a + 1 - query(a , n , 1) ;

    if(!nn){//如果区间内没有0的位置了,那么就直接输出。

        Noanswer() ;

        return ;

    }

    while(r >= l){//二分首位置

        int mid = l + r >> 1 ;

        int now = mid - a + 1 - query(a ,mid ,1) ;

        if(now >= 1){

            pos1 = min(pos1 ,mid) ;

            r = mid - 1 ;

        }

        else l = mid + 1 ;

    }

    int pos2 = inf ;

    nn = n - pos1 + 1 - query(pos1 , n ,1) ;

    if(nn <= b){//如果剩余的0的个数小于等于b的数量,那么需要找出最后一个0的位置。

        int l = pos1 , r = n ;

        while(r >= l){//二分末位置

            int mid = r + l >> 1 ;

            int now = mid - pos1 + 1 - query(pos1 , mid , 1) ;

            if(now == nn){

                pos2 = min(pos2 , mid) ;

                r = mid - 1 ;

            }

            else l = mid + 1 ;

        }

        answer(pos1 - 1, pos2 - 1) ;

        update(pos1, pos2 , 1 , 1) ;

    }

    else {//其实我觉得这个二分和上面那个可以合并的,我懒得改了。

        int l = pos1 , r = n ;



        while(r >= l){//二分末位置

            int mid = l + r >> 1 ;

            int now = mid - pos1 + 1 - query(pos1, mid, 1) ;

            if(now == b){

                pos2 = min(pos2 ,mid) ;

                r = mid - 1 ;

            }

            else if(now > b)r = mid - 1 ;

            else l = mid + 1 ;

        }

        answer(pos1 - 1, pos2 - 1 ) ;

        update(pos1 ,pos2 ,1, 1) ;

    }

}

void solve2(int a , int b){

    answer(query(a , b , 1)) ;

    update(a , b, 1 , 2) ;

}

int main() {

    int T ;

    cin >> T ;

    int ss = 0 ;

    while( T -- ){

        scanf("%d%d",&n,&m) ;

        init() ;

        build(1 ,n , 1) ;

        while(m -- ){

            //debug(1) ;

            int a , b , c ;

            RD(a) ;

            RD(b) ;

            RD(c) ;

            if(a == 1){

                b ++ ;

                solve1(b , c) ;

            }

            else if(a == 2){

                b ++ , c ++ ;

                solve2(b , c) ;

            }

        }

        puts("") ;

    }

    return 0 ;

}


 

 

你可能感兴趣的:(HDU)