【ZJOI2013】K 大数查询 ( 树状数组套线段树 )

Description

有n 个位置和m 个操作。操作有两种,每次操作如果是1 a b c 的形式,表示往第a 个位置到第b 个位置每个位置加入一个数c。如果操作形如2 a b c 的形式,表示询问从第a 个位置到第b 个位置,第c 大的数是多少。

Input

在输入文件sequence.in 中,第一行两个数n,m。意义如题目描述。
接下来m 行每行形如1 a b c 或者2 a b c 如题目描述。

Output

在输出文件sequence.out 中,对于每个询问回答k 大数是多少。

Sample Input

2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3

Sample Output

1
2
1

Data Constraint

30%的数据n=m=1000
100%的数据n,m≤50000,并且后7 个点的数据n,m 的范围从32000 到50000近似成等差数列递增。a≤b≤n,1 操作中|c|≤n,2 操作中|c|≤maxlongint

嗯 , 应该要自然地想到二分答案 , 然后呢?
判定性问题 , 我想知道对于二分出的x,在区间l,r中有多少大于x的数的个数。
那应该想到开一个权值树状数组,里面套个线段树记录位置。
线段树要动态开节点, 为了方便, 我们不采取下传标记方式,比较麻烦。
对于线段树每个节点,(像昨天做的线段数套单调队列一样 )开两个变量,
cover 与pass
对于插入,遇到区间完整覆盖的,cover ++ , 对于路过的节点pass都加上其目标节点的区间大小( _r - _l + 1 )。
对于查询,遇到区间完整覆盖的,直接返回其pass值,其余非完整覆盖的,返回cover * ( _r - _l + 1 ) + Ask_Value_of_SubNode
这种线段树的打法避免了平常打法的复杂的标记。

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std ;

#define N 50010
#define lowbit( x ) x & -x

int i , j , k , n , m , T ;

struct SegmentTree {
    int l , r ;
    int cover , pass ;
}tr[13183043] ;

int rot[N] ;

struct data {
    int typ , c , a , b ;
}a[N] ;

struct rec {
    int ps , va ;
}d[N] ;

bool cmp( rec a , rec b ) {
    return a.va < b.va ;
}

int disva = 0 ;

int demap[N] ;

void SegIns( int l , int  r, int _l , int _r , int po ) {
    int m = l + r >> 1 ;
    tr[po].pass += _r - _l + 1 ;
    if( _l==l && _r==r ) {
        tr[po].cover +=  1 ;
        return ;
    }
    if( _r<=m ) SegIns( l , m , _l , _r , ( tr[po].l==0 ? tr[po].l = ++ T : tr[po].l ) )  ;
        else if( _l>m ) SegIns( m+1 , r , _l , _r , ( tr[po].r==0 ? tr[po].r = ++ T : tr[po].r ) ) ;
            else SegIns( l , m , _l , m , ( tr[po].l==0 ? tr[po].l = ++ T : tr[po].l ) ) ,
              SegIns( m+1 , r , m+1 , _r , ( tr[po].r==0 ? tr[po].r = ++ T : tr[po].r ) ) ;   

}

int SegAsk( int l , int r , int _l , int _r , int po ) {
    int m = l + r >> 1 ;
    if( po==0 ) return 0 ;
    if( _l==l && _r==r ) return tr[po].pass  ;
    if( _r<=m ) return SegAsk( l , m , _l , _r , tr[po].l ) + tr[po].cover * ( _r - _l + 1 )  ;
        else if( _l>m ) return SegAsk( m+1 , r , _l , _r , tr[po].r ) + tr[po].cover * ( _r - _l + 1 ) ;
            else return SegAsk( l , m , _l , m , tr[po].l ) +  SegAsk( m+1 , r , m+1 , _r , tr[po].r ) + tr[po].cover * ( _r - _l + 1 ) ;
}


void Insert( int x , int l , int r ) {
    for( ;  x>0 ; x-=lowbit( x ) ) {
        if( rot[x]==0 ) rot[x] = ++ T ;
        SegIns( 1 , n , l , r , rot[x] ) ;
    }
}

bool Ask( int x , int l , int r , int va ) {
    int ret = 0 ;
    for( ; x<=disva ; x+=lowbit( x ) ) {
        ret += SegAsk( 1 , n , l , r , rot[x] ) ;
    }
    return ret >= va ;
}

int tot = 0 ;

int main() {
    scanf("%d%d",&n,&m ) ;
    for( i=1 ; i<=m ; i++ ) {
        scanf("%d%d%d%d",&a[i].typ , &a[i].a , &a[i].b , &a[i].c ) ;
        if( a[i].typ==1 ) d[++tot].ps = i , d[tot].va = a[i].c ;
    }
    sort( d+1 , d+1+tot , cmp ) ;
    d[0].va = -0x7fffffff ;
    for( i=1 ; i<=tot ; i++ ) {
        if( d[i].va != d[i-1].va ) ++disva , demap[ disva ] = d[i].va ;
        a[ d[i].ps ].c = disva ;
    }
    for( i=1 ; i<=m ; i++ ) {
        if( a[i].typ==1 ) {
            // insert
            Insert( a[i].c , a[i].a , a[i].b ) ;
        } else {
            int L = 1 , R = disva ;
            while( L<=R ) {
                int M = L+R >> 1 ;
                if( Ask( M , a[i].a , a[i].b , a[i].c ) ) L = M + 1 ; else R = M - 1 ;
            }
            printf("%d\n",demap[R] ) ;
        }
    }
}

Debuglog
把询问的c也离散化了。。。。。傻逼的可以

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值