【NOIP2016模拟7.11】排序

Description

这里写图片描述

Input

这里写图片描述

Output

拍完序后第k位上的数字

Sample Input

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

Sample Output

5

Data Constraint

30%:n,m<=300
100% n,m<=100000

Solution

对于30分,直接按照操作暴力即可,然而数据太水,可以水出60分。
对于100%显然是不能直接暴力的。可以想到二分答案。
分析复杂度,发现对于每次操作,必须不大于 log2(n) 才能,那么想到了什么?线段树!
对于二分的答案,将大于这个的数变为1,小于的变为0。对于一个区间,从小到大排序就是把0堆在前面,1放在后面,从大到小排序则相反。这可以用线段树区间修改来做到。
最后再按照第k位看看是0还是1来修改区间。
注意线段树的归零

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1010000
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,m,a[N],c[N],t[N*2][2],lazy[N*2];
struct noo{
    int o,x,y;
};
noo q[N];
void down(int v,int i,int j)
{
    int m=(i+j)/2;
    t[v*2][lazy[v]]=(m-i+1);t[v*2][1-lazy[v]]=0;
    t[v*2+1][lazy[v]]=(j-m);t[v*2+1][1-lazy[v]]=0;
    lazy[v*2]=lazy[v*2+1]=lazy[v];lazy[v]=-1;
}
void build(int v,int i,int j)
{
    lazy[v]=-1;
    if (i==j) {t[v][c[i]]=1;t[v][1-c[i]]=0;return;}
    build(v*2,i,(i+j)/2);build(v*2+1,(i+j)/2+1,j);
    t[v][0]=t[v*2][0]+t[v*2+1][0];
    t[v][1]=t[v*2][1]+t[v*2+1][1];
}
int find(int v,int i,int j,int x,int y)
{
    if(i==x&&j==y){return t[v][0];}
    int m=(i+j)/2;if(lazy[v]!=-1)down(v,i,j);
    if(y<=m) return find(v*2,i,m,x,y);
    else if(x>m) return find(v*2+1,m+1,j,x,y);
         else return find(v*2,i,m,x,m)+find(v*2+1,m+1,j,m+1,y);
}
void change(int v,int i,int j,int x,int y,int z)
{
    if(i==x&&j==y) {lazy[v]=z;t[v][z]=y-x+1;t[v][1-z]=0;return;}
    int m=(i+j)/2;if (lazy[v]!=-1) down(v,i,j);
    if(y<=m) change(v*2,i,m,x,y,z);
    else if(x>m) change(v*2+1,m+1,j,x,y,z);
         else change(v*2,i,m,x,m,z),change(v*2+1,m+1,j,m+1,y,z);
    t[v][0]=t[v*2][0]+t[v*2+1][0];
    t[v][1]=t[v*2][1]+t[v*2+1][1];
}
int main()
{
    freopen("4605.in","r",stdin);
//  freopen("4605.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,m)
    {
        int op,x,y;scanf("%d%d%d",&q[i].o,&q[i].x,&q[i].y);
    }
    int qq;scanf("%d",&qq);
    memset(lazy,-1,sizeof(lazy));
    int l=1,r=n;
    for(;l+1<r;)
    {
        int mid=(l+r)/2;
        fo(i,1,n) c[i]=a[i]>=mid?1:0;
        build(1,1,n);
        fo(i,1,m){
            int k=find(1,1,n,q[i].x,q[i].y);
            int jy=q[i].o;if(jy==1) k=(q[i].y-q[i].x+1)-k;
            if (k!=q[i].y-q[i].x+1&&k!=0) change(1,1,n,q[i].x,q[i].x+k-1,jy),change(1,1,n,q[i].x+k,q[i].y,1-jy);
        }
        int k=1-find(1,1,n,qq,qq);
        if(k==1) l=mid;else r=mid;
    }
    int mid=l;
    fo(i,1,n) c[i]=a[i]>=mid?1:0;
    build(1,1,n);
    fo(i,1,m){
        int k=find(1,1,n,q[i].x,q[i].y);
        int jy=q[i].o;if(jy==1) k=(q[i].y-q[i].x+1)-k;
        if (k!=q[i].y-q[i].x+1&&k!=0) change(1,1,n,q[i].x,q[i].x+k-1,jy),change(1,1,n,q[i].x+k,q[i].y,1-jy);
    }
    int k=1-find(1,1,n,qq,qq);  
    if(k==1) r=l;
    printf("%d",r);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值