2018 UESTC Training for Data Structures 一棵复杂的线段树(线段树)

题目链接:http://qscoj.cn/#/problem/show/1919

题目大意:中文题就不过多做解释了 -,-

题目思路:一开始这个题是挺懵逼的,在脑海里过了一遍学过的数据结构,好像并没有那个可以满足高效率的区间排序操作的(可能是我太菜了)。去看了别人的做法之后才知道原来线段树也是可以进行排序操作的。

因为这题的排序是乱序且没有规律的,所以本题很难直观地找到通解,所以我们可以考虑二分答案(因为是1~n的排列,所以必然是可以找到正解的),二分出当前的值mid之后,我们考虑用线段树来维护原数组中的值的情况,在线段树中把大于mid的值的位置设为1,小于等于mid的值的位置设为0,这样每次进行排序操作的时候我们就可以通过线段树的区间查询找出当前区间中有多少个1(也就是当前区间有多少个大于mid的数),然后再根据从大到小排序或者从小到大排序,将区间中的值排序好(这个排序可以借用线段树的区间更新功能,比如要进行从小到大排序,假设在区间[l,r]中有num个大于mid的值,那么就可以将区间[l,l+num-1]上的每一位更新为0,区间[l+num,r]的每一位更新为1),将所有排序操作都完成之后,再对第k位的值进行查询,如果为1,说明当前的mid是小于正解的,如果为0,说明当前mid是大于等于正解的,按这个思路不断进行二分求解,最后就可以求出正解了。时间复杂度大概是O(q*logn*logn)

具体实现看代码:

#include 
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define debug(x) cout<<"["<pii;
const int MX=1e5+7;

int n,q,k;
int mid,cnt;
int sum[MX<<2],tag[MX<<2],a[MX];
struct que{
    int op,l,r;
}qq[MX];
void push_up(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int l,int r,int rt){
    if(tag[rt]!=-1){
        tag[rt<<1]=tag[rt];tag[rt<<1|1]=tag[rt];
        int m=(l+r)>>1;
        sum[rt<<1]=tag[rt]*(m-l+1);
        sum[rt<<1|1]=tag[rt]*(r-m);
        tag[rt]=-1;
    }
}
void build(int l,int r,int rt){
    tag[rt]=-1;
    if(l==r){
        if(a[++cnt]>mid)
            sum[rt]=1;
        else
            sum[rt]=0;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}
void update(int L,int R,int d,int l,int r,int rt){
    if(L<=l && r<=R){
        tag[rt]=d;
        sum[rt]=d*(r-l+1);
        return;
    }
    push_down(l,r,rt);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,d,lson);
    if(R>m) update(L,R,d,rson);
    push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l && r<= R) return sum[rt];
    push_down(l,r,rt);
    int m=(l+r)>>1,res=0;
    if(L<=m) res+=query(L,R,lson);
    if(R>m) res+=query(L,R,rson);
    return res;
}

int main(){
    //FIN;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
        scanf("%d%d%d",&qq[i].op,&qq[i].l,&qq[i].r);
    int L=1,R=n;
    while(L>1;
        build(1,n,1);
        for(int i=1;i<=q;i++){
            if(qq[i].op==1){
                int l=qq[i].l,r=qq[i].r;
                int num=query(l,r,1,n,1);
                if(num==0) continue;
                update(l,l+num-1,1,1,n,1);
                update(l+num,r,0,1,n,1);
            } else{
                int l=qq[i].l,r=qq[i].r;
                int num=query(l,r,1,n,1);
                num=(r-l+1)-num;
                if(num==0) continue;
                update(l,l+num-1,0,1,n,1);
                update(l+num,r,1,1,n,1);
            }
        }
        if(query(k,k,1,n,1)==1)
            L=mid+1;
        else
            R=mid;
    }
    printf("%d\n",L);
    return 0;
}

 

你可能感兴趣的:(数据结构,ACM)