bzoj4552 [Tjoi2016&Heoi2016]排序(二分答案+线段树)

这题神啊orz
我们二分答案,把大于等于x的变成1,小于x的变成0。对于01序列去排序我们就可以直接得出排序结果啦!于是我们用线段树维护一下,每次把l~r升序排序,就先统计出l~r有多少个0,多少个1,然后把0都放在前面即可。降序同理。

#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,a[N],pos,ans;
struct node{
    int n0,n1,tag;
    friend node operator+(node a,node b){
        node res;res.tag=-1;
        res.n0=a.n0+b.n0;res.n1=a.n1+b.n1;return res;
    }
}tr[N<<2];
struct quer{
    int op,l,r;
}qq[N];
inline void pushdown(int p,int l,int r){
    if(tr[p].tag==-1) return;tr[p<<1].tag=tr[p<<1|1].tag=tr[p].tag;
    int mid=l+r>>1,len=mid-l+1;
    if(!tr[p].tag){
        int num=tr[p].n0>=len?len:tr[p].n0;
        tr[p<<1].n0=num;tr[p<<1].n1=len-num;
        tr[p<<1|1].n0=tr[p].n0-tr[p<<1].n0;
        tr[p<<1|1].n1=tr[p].n1-tr[p<<1].n1;
    }else{
        int num=tr[p].n1>=len?len:tr[p].n1;
        tr[p<<1].n1=num;tr[p<<1].n0=len-num;
        tr[p<<1|1].n0=tr[p].n0-tr[p<<1].n0;
        tr[p<<1|1].n1=tr[p].n1-tr[p<<1].n1;
    }tr[p].tag=-1;
}
inline void build(int p,int l,int r){
    tr[p].n0=tr[p].n1=0;tr[p].tag=-1;
    if(l==r){a[l]>=ans?tr[p].n1=1:tr[p].n0=1;return;}
    int mid=l+r>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);
    tr[p]=tr[p<<1]+tr[p<<1|1];
}
inline void change(int p,int l,int r,int x,int y,int op,int n0,int n1){
    if(x==l&&r==y){tr[p].tag=op;tr[p].n0=n0;tr[p].n1=n1;return;}
    int mid=l+r>>1;pushdown(p,l,r);
    if(y<=mid) change(p<<1,l,mid,x,y,op,n0,n1);
    else if(x>mid) change(p<<1|1,mid+1,r,x,y,op,n0,n1);
    else{
        if(!op){
            int len=mid-x+1,num=n0>=len?len:n0;
            change(p<<1,l,mid,x,mid,op,num,len-num);
            change(p<<1|1,mid+1,r,mid+1,y,op,n0-num,n1-(len-num));
        }else{
            int len=mid-x+1,num=n1>=len?len:n1;
            change(p<<1,l,mid,x,mid,op,len-num,num);
            change(p<<1|1,mid+1,r,mid+1,y,op,n0-(len-num),n1-num);
        }
    }tr[p]=tr[p<<1]+tr[p<<1|1];
}
inline node ask(int p,int l,int r,int x,int y){
    if(x==l&&r==y) return tr[p];
    int mid=l+r>>1;pushdown(p,l,r);
    if(y<=mid) return ask(p<<1,l,mid,x,y);
    if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
    return ask(p<<1,l,mid,x,mid)+ask(p<<1|1,mid+1,r,mid+1,y);
}
inline bool ask1(int p,int l,int r,int x){
    if(l==r) return tr[p].n1;
    int mid=l+r>>1;pushdown(p,l,r);
    if(x<=mid) return ask1(p<<1,l,mid,x);
    return ask1(p<<1|1,mid+1,r,x);
}
inline bool jud(int x){
    ans=x;build(1,1,n);
    for(int i=1;i<=m;++i){
        node res=ask(1,1,n,qq[i].l,qq[i].r);
        change(1,1,n,qq[i].l,qq[i].r,qq[i].op,res.n0,res.n1);
    }return ask1(1,1,n,pos);
}
int main(){
//  freopen("sort.in","r",stdin);
//  freopen("sort.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i) qq[i].op=read(),qq[i].l=read(),qq[i].r=read();
    pos=read();int l=1,r=n;
    while(l<=r){
        int mid=l+r>>1;
        if(jud(mid)) l=mid+1;
        else r=mid-1;
    }printf("%d\n",l-1);
    return 0;
}

你可能感兴趣的:(bzoj,二分答案,线段树)