ZOJ 2112 [树状数组套主席树]

问题: 给定一个长N<=50000的序列,允许有两种操作:  ①询问第l至第r个之间的第k大的数? ②将位置i的数改为y。

分析:若是不需要修改可采用划分树做。线段树或splay可以查找第k大数,而区间[l , r]之间只需处理前缀和即可,但朴素实现的前缀和修改复杂的需要O(n),若采用树状数组只需O(logn) , 故只需用树状数组套主席树即可。不过ZOJ这题内存卡得很紧,直接在数组数组上插入原始序列需要O(n*logn*logn)空间复杂的肯定MLE ,  但是修改操作却相对较少,我们可以将他们分开操作  , 用一个类似前缀数组记录原始序列的前缀和 , 而用数装数组处理后面的操作 。 空间只需O(n*logn + m*logn*logn)  。 

还有其他做法,像线段树套splay : http://www.cnblogs.com/staginner/archive/2012/03/22/2411993.html

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL ;
const int null = 0 ;
const int maxn = 60010 ;
const int maxm = 1800000 ;

int n , m ;
int value[maxn] ,val[maxn], N ;
struct Oper{
    char cmd ;
    int l , r , k ;
    Oper(){} ;
    Oper(char cmd , int l ,int r , int k): cmd(cmd) , l(l) , r(r) , k(k){} ;
}q[10010];  int qn ;

int tree[maxn] ,S[maxn], te[maxn] , lson[maxm] , rson[maxm] , size[maxm] ,tot;

void init(){
    tot = 0 ;
    size[null]=lson[null] = rson[null]= 0 ;
    for(int i=0; i<=n; i++) tree[i] = null ;
    S[0] = 0 ;
}

inline int lowbit(int x) {
    return x & -x ;
}
void insert(int pre , int &o, int L ,int R ,int x ,int val){
    int mid= (L+R)>>1 ;
    o = ++ tot;
    lson[o] = lson[pre] ;
    rson[o] = rson[pre] ;
    size[o] = size[pre] + val ;
    if(L==R)return ;
    if(x <= mid) insert(lson[pre] , lson[o] , L , mid , x , val) ;
    else insert(rson[pre] , rson[o] , mid+1 , R , x , val) ;
}
void add(int pos ,int x, int val) {
    for(int i=pos; i<=n; i+=lowbit(i)) {
        insert(tree[i] , tree[i] , 1 , N , x , val) ;
    }
}
int sum_lson(int x) {
    int ret = 0 ;
    for(int i=x; i; i-=lowbit(i)) {
        ret += size[lson[te[i]]] ;
    }
    return ret ;
}
int query(int l , int r, int sl, int sr , int k) {
    for(int i=l; i; i-=lowbit(i)) te[i] = tree[i] ;
    for(int i=r; i; i-=lowbit(i)) te[i] = tree[i] ;
    int L = 1 , R = N ;
    while(L < R){
        int mid =(L+R)>>1 ;
        int sz = sum_lson(r) - sum_lson(l) + size[lson[sr]] - size[lson[sl]] ;
        if( k<= sz) {
            R = mid ;
            sl = lson[sl] , sr = lson[sr] ;
            for(int i=l; i; i-=lowbit(i)) te[i] = lson[te[i]] ;
            for(int i=r; i; i-=lowbit(i)) te[i] = lson[te[i]] ;
        }
        else {
            L = mid + 1 ;
            k -= sz ;
            sl = rson[sl] , sr= rson[sr] ;
            for(int i=l; i; i-=lowbit(i)) te[i] = rson[te[i]] ;
            for(int i=r; i; i-=lowbit(i)) te[i] = rson[te[i]] ;
        }
    }
    return value[L-1] ;
}

int main()
{
  //  freopen("in.txt", "r" ,stdin) ;
    int T ;
    scanf("%d" , &T) ;
    while(T--) {
        scanf("%d%d" ,&n ,&m) ;
        init() ;
        N = 0 ;
        for(int i=0; i<n; i++) {
            scanf("%d" , &val[i]) ;
            value[N++] = val[i] ;
        }
        qn = 0;
        for(int i=0; i<m; i++) {
            char str[5] ;
            scanf("%s" , str) ;
            int l ,r ,k;
            if(str[0] == 'Q') {
                scanf("%d%d%d" , &l , &r , &k) ;
                q[qn++] = Oper(str[0] , l , r ,k) ;
            }
            else {
                scanf("%d%d" ,&l , &r);
                q[qn++] = Oper(str[0] , l , r , 0) ;
                value[N++] = q[qn-1].r ;
            }
        }
        sort(value , value+N);
        N = unique(value , value+N) - value ;
        for(int i=0; i<n; i++) val[i] = lower_bound(value , value+N , val[i]) - value + 1;
        for(int i=0; i<qn; i++) if(q[i].cmd == 'C') {
            q[i].r = lower_bound(value , value+N , q[i].r) - value + 1 ;
        }
        for(int i=0; i<n; i++) {
            //add(i+1 , val[i] , 1) ;
            insert(S[i] , S[i+1] , 1 ,N , val[i] , 1) ;
        }
        for(int i=0; i<qn; i++) {
            if(q[i].cmd == 'Q') {
                int ans = query(q[i].l-1 , q[i].r ,S[q[i].l-1] , S[q[i].r],  q[i].k) ;
                printf("%d\n" , ans);
            }
            else {
                add(q[i].l , val[q[i].l - 1] , -1) ;
                add(q[i].l , q[i].r , 1) ;
                val[q[i].l- 1] = q[i].r ;
            }
        }

      //   printf("tot = %d\n" , tot) ;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值