数据结构训练之四

https://www.luogu.org/problem/P2824#submit

做法:

由于将一个普通序列排序很慢,需要nlogn的时间,所以我们试着把它转化为对01序列排序。先来考虑一个简单的问题:


  • 如何将一个01序列排序?(logn的复杂度)
  • 对于这个问题,我们使用线段树来维护。查询一段区间内的1的个数记为cnt1,如果是升序,就将这段区间的[r-cnt1+1, r]都更改为1,将[l, r-cnt1]更改为0。降序则将[l, l+cnt1-1]更改为1,将[l+cnt, r]更改为0。这样我们就成功地把排序转化为了区间查询和区间修改。

接下来我们来说本题的做法:

这是一个离线的做法。首先二分答案mid。我们把原排列中大于等于mid的数都标记为1,小于mid的都标记为0。然后对于每个操作我们就将01序列排个序。最后如果第p个位子仍是1的话就是可行的。

这个二分成立因为是满足单调性的:可以简单地假设一下,如果你二分的答案是1,那么原序列所有的值都转化为了1,所以最后肯定是true。如果二分一个值成立当且仅当这个位子的值大于等于mid,故如果check返回true,则l = mid+1,否则r = mid-1。

(这题的思想可以借鉴,比较巧妙)

code by wzxbeliever:

#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e5+5;
int n,m,id,ans,mm; 
int val[maxn],tree[maxn<<2],tag[maxn<<2];
struct node{
    int op,LL,RR;
}Q[maxn];
il void up(int rt){tree[rt]=tree[rt<<1]+tree[rt<<1|1];return;}
il void bulid(int l,int r,int rt){
    if(l==r){tree[rt]=val[l]>=mm;tag[rt]=0;return;}
    int mid=l+r>>1;
    bulid(l,mid,rt<<1);
    bulid(mid+1,r,rt<<1|1);
    up(rt);tag[rt]=0;//多次记得每次清空 0---->什么也不干 ;1----->赋值为1;2----->赋值为0 
    return;
}
il void modify(int l,int r,int rt,int p){
    if(!p)return;tag[rt]=p;
    if(p==1)tree[rt]=r-l+1;
    else tree[rt]=0; 
}
il void down(int l,int r,int rt){
    if(!tag[rt])return;
    int mid=l+r>>1;
    modify(l,mid,rt<<1,tag[rt]);
    modify(mid+1,r,rt<<1|1,tag[rt]);
    tag[rt]=0;
} 
il int query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R)return tree[rt];
    int mid=l+r>>1;
    int tot=0;
    down(l,r,rt);
    if(L<=mid)tot+=query(l,mid,rt<<1,L,R);
    if(mid<R)tot+=query(mid+1,r,rt<<1|1,L,R);
    return tot;
}
il void upd(int l,int r,int rt,int L,int R,int p){
    if(L<=l&&r<=R){modify(l,r,rt,p);return;}
    int mid=l+r>>1;
    down(l,r,rt);
    if(mid>=L)upd(l,mid,rt<<1,L,R,p);
    if(mid<R)upd(mid+1,r,rt<<1|1,L,R,p);
    up(rt);
}
il int querypoint(int l,int r,int rt,int pos){
    if(l==r)return tree[rt];
    int mid=l+r>>1;
    down(l,r,rt);
    if(pos<=mid)return querypoint(l,mid,rt<<1,pos);
    else return querypoint(mid+1,r,rt<<1|1,pos);
}
il bool check(){
    bulid(1,n,1);
    for(ri i=1;i<=m;i++){
        int L=Q[i].LL,R=Q[i].RR;
        int cnt=query(1,n,1,L,R);
        if(cnt==0)continue;//这个句子很关键,不然会越界re4个点
        if(Q[i].op==0){
            upd(1,n,1,L,R-cnt,2);
            upd(1,n,1,R-cnt+1,R,1);
        }
        else {
            upd(1,n,1,L,L+cnt-1,1);
            upd(1,n,1,L+cnt,R,2);
        }
    }
    if(querypoint(1,n,1,id)==1)return true;
    else return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(ri i=1;i<=n;i++)scanf("%d",&val[i]);
    for(ri i=1;i<=m;i++)scanf("%d%d%d",&Q[i].op,&Q[i].LL,&Q[i].RR);
    scanf("%d",&id);
    int l=1,r=n;
    while(l<=r){
        mm=l+r>>1;
        if(check())ans=mm,l=mm+1;
        else r=mm-1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzxbeliever/p/11748618.html
今日推荐