[HEOI2016/TJOI2016]排序

题意略。

solution

这道题目我们可以线段树加二分,
首先我们二分最终的答案,然后我们把这个序列变成0/1序列,具体而言,我们将大于等于mid的设为1,小于mid的设为0,排序的话,以升序为例,我们把0扔到左边,1扔到右边,降序类似,然后我们看一下最后我们要求的位置上是0还是1,同时缩小答案范围即可。
code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=1e+5+55;
int s[N*4],setv[N*4],x[N],y[N],a[N],b[N],z[N];
int l,r,n,m,mid,pos,k,p,y1,y2,w;
void build(int o,int l,int r)
{
    if (l==r){
        setv[o]=s[o]=b[l];
        return;
    }
    int m=l+r>>1;
    build(o<<1,l,m);build(o<<1|1,m+1,r);
    s[o]=s[o<<1]+s[o<<1|1];
}
void pushdown(int o)
{
    if (setv[o]>=0)
    {
        setv[o<<1]=setv[o<<1|1]=setv[o];
        setv[o]=-1;
    }
}
void maintain(int o,int l,int r)
{
    if (r>l) s[o]=s[o<<1]+s[o<<1|1];
    if (setv[o]>=0) s[o]=setv[o]*(r-l+1);
}
void update(int o,int l,int r)
{
    if (l>y2||r<y1) return;//注意一定要加
    if (y1<=l&&r<=y2) setv[o]=w;
    else 
    {
        int m=l+r>>1;
        pushdown(o);
        if (y1<=m) update(o<<1,l,m);else maintain(o<<1,l,m);
        if (m<y2) update(o<<1|1,m+1,r);else maintain(o<<1|1,m+1,r);
    }
    maintain(o,l,r);
}
void query(int o,int l,int r)
{
    if (l>y2||r<y1) return;
    if (setv[o]>=0) k+=(min(y2,r)-max(l,y1)+1)*setv[o];
    else
    {
        if ((y1<=l)&&(r<=y2)) k+=s[o];
        else
        {
            int m=l+r>>1;
            if (y1<=m) query(o<<1,l,m);
            if (m<y2) query(o<<1|1,m+1,r);
        }
    }
}
int main()
{
    //freopen("jzoj4605.in","r",stdin);
    //freopen("jzoj4605.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,m) scanf("%d%d%d",&z[i],&x[i],&y[i]);
    scanf("%d",&p);
    l=1;
    r=n;
    int ans=0;
    while (l<r)
    {
        mid=l+r>>1;
        fo(i,1,n) 
        if (mid<=a[i]){
            b[i]=1; 
            if (mid==a[i]) pos=i;
        }
        else b[i]=0;
        memset(setv,-1,sizeof(setv));
        build(1,1,n);
        fo(i,1,m){
            y1=x[i],y2=y[i],k=0;
            query(1,1,n);
            if (!z[i]){
                y1=x[i],y2=y[i]-k,w=0;
                update(1,1,n);
                //update(x[i],y[i]-k,0);//1
                y1=y[i]-k+1,y2=y[i],w=1;
                update(1,1,n);
                //update(y[i]-k+1,y[i],1);
                if (x[i]<=pos&&pos<=y[i]) pos=y[i]-k+1;// 1
            }
            else{
                y1=x[i],y2=x[i]+k-1,w=1;
                update(1,1,n);
                //update(x[i],x[i]+k-1,1);//2
                //update(x[i]+k,y[i],0);
                y1=x[i]+k,y2=y[i],w=0;
                update(1,1,n);
                if (x[i]<=pos&&pos<=y[i]) pos=x[i]+k-1;// 2
            }
        }
        y1=y2=p;
        k=0;
        query(1,1,n);
        if (k==1) l=mid+1;
        else r=mid;
    }
    printf("%d",l-1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值