hdu5412--CRB and Queries(整体二分)

题目链接:点击打开链接

题目大意:给出n个数的初始序列,有两种操作,1 l v将第l个数换成v,2 l r k 问在区间[l,r]内的第k大是多少,并输出

经典的题目,但是树状数组+主席树(TLE)伸展树(MLE),听说他们用的块状链表,zhazha表示不会,后来补题,发现整体二分是一个好方法。

首先,这个整体二分是将数据范围和操作放到一起,不断二分数据的范围,可以得到有某些操作可以被完成,有某些操作不可以被完成。

例如:当前数据范围的区间是[l,r],那么mid = (l+r)/2 ,所有修改操作如果修改的数小于等于mid,那么就是可以被完成的,那么就修改数,否则就是不能被完成的。对于查询操作,如果查询到的范围内的数的个数x小于k,那么要查询的数是不在区间[1,5]内的,所以这个操作是不会再[l,mid]中被完成,将该操作的k改为k-x。否则就会在[l,mid]内被完成。按照数据范围向下二分得到[l,mid],[mid+1,r],将能被完成的操作放入左区间,不能完成的操作放入右区间。(注意放到同一区间的的操作的相对顺序是不能被改变的),这样最终的查询操作都会被确定到一个数值,这个数值就是要输出的值。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define maxn 300100
struct node{
    int type, l, r, v , ans;
}p[maxn];
int c[maxn], n, q, cnt, max1;
int a[maxn];
int id1[maxn], id2[maxn];
void p_add(int type, int l,int r, int v) {
    p[cnt].type = type;
    p[cnt].l = l; p[cnt].r = r;
    p[cnt].v = v;
    id1[cnt] = cnt;
    cnt++;
}
int lowbit(int x) {
    return x & -x ;
}
void add(int i,int k) {
    while( i <= n ) {
        c[i] += k ;
        i += lowbit(i) ;
    }
}
int sum(int i) {
    int num = 0 ;
    while( i ) {
        num += c[i] ;
        i -= lowbit(i) ;
    }
    return num ;
}
void solve(int L, int R, int low, int high) {
    if( L > R ) return;
    if( low == high ) {
        while(L <= R) {
            if( p[ id1[L] ].type == 2 ) p[ id1[L] ].ans = low;
            L++;
        }
        return ;
    }
    int i, j, num, l = L, r = R;
    int mid = (low + high)/2;
    for(i = L; i <= R; i++) {
        j = id1[i];
        if( p[j].type == 2 ) {
            num = sum(p[j].r) - sum(p[j].l-1);
            if( num < p[j].v ) {
                p[j].v -= num;
                id2[r--] = j;
            }
            else
                id2[l++] = j;
        }
        else {
            if( p[j].v <= mid ) {
                add(p[j].l,p[j].type);
                id2[l++] = j;
            }
            else
                id2[r--] = j;
        }
    }
    for(i = L; i <= R; i++) {
        j = id1[i];
        if( p[j].type != 2 && p[j].v <= mid ) add(p[j].l,-p[j].type);
    }
    for(i = L; i < l; i++)
        id1[i] = id2[i];
    for(r = R; i <= R; r--, i++)
        id1[i] = id2[r];
    solve(L,l-1,low,mid);
    solve(l,R,mid+1,high);
}
int main() {
    int i, j, type, l, r, v;
    while( scanf("%d", &n) != EOF ) {
        memset(c,0,sizeof(c));
        cnt = max1 = 0;
        for(i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            p_add(1, i, i, a[i]);
            max1 = max(max1, a[i]);
        }
        scanf("%d", &q);
        while( q-- ) {
            scanf("%d", &type);
            if( type == 1 ) {
                scanf("%d %d", &l, &v);
                p_add(-1, l, l, a[l]);
                a[l] = v;
                p_add(1, l, l, a[l]);
                max1 = max(max1, a[l]);
            }
            else {
                scanf("%d %d %d", &l, &r, &v);
                p_add(2, l, r, v);
            }
        }
        solve(0,cnt-1,0,max1) ;
        for(i = 0; i < cnt; i++) {
            if( p[i].type == 2 )
                printf("%d\n", p[i].ans);
        }
    }
    return 0 ;
}


你可能感兴趣的:(hdu5412--CRB and Queries(整体二分))