JZOJ.4605. 排序

Problem

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

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

这里写图片描述

Solution

正解:线段树+二分。
先二分答案mid,按照mid建一棵线段树(a[i]中如果<mid则标记为0,≥mid则标记为1),将区间[l,r]的数排序,本质是将0放到前面,1放到后面(或将0放到后面,1放到前面)。
实现方法:如果是升序,那么先搜索区间[l,r]中0的个数k,然后将前面的k个数变为0,后(r-l+1)-k个数变成1。用线段树维护。如果是降序就相反。
最后看看第q个数是0还是1。如果是0向前二分,反之向右。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define N 100010
using namespace std;
int i,j,q,op,n,m,k,kk,mid,temp;
int tree[N*10][5],a[N],b[N][3];
void build(int s,int l,int r,int mid)
{
    if (l==r)
    {
        if (a[l]>=mid) tree[s][0]=tree[s][1]=1;
                  else tree[s][0]=tree[s][1]=0;
        tree[s][2]=-1;
        tree[s][3]=l;
        tree[s][4]=r;
        return;
    }
    int wz=(l+r)/2;
    build(s*2,l,wz,mid);
    build(s*2+1,wz+1,r,mid);
    tree[s][1]=tree[s*2][1]+tree[s*2+1][1];
    tree[s][2]=-1;
    tree[s][3]=tree[s*2][3];
    tree[s][4]=tree[s*2+1][4];
}
void count(int s,int l,int r,int x,int y)
{
    if (l==x && r==y)
    {
        k+=tree[s][1];
        return;
    }
    if (tree[s][2]!=-1)
    {
        tree[s*2][0]=tree[s][2];
        tree[s*2][1]=tree[s][2]*(tree[s*2][4]-tree[s*2][3]+1);
        tree[s*2][2]=tree[s][2];
        tree[s*2+1][0]=tree[s][2];
        tree[s*2+1][1]=tree[s][2]*(tree[s*2+1][4]-tree[s*2+1][3]+1);
        tree[s*2+1][2]=tree[s][2];
        tree[s][2]=-1;
    }
    int wz=(l+r)/2;
    if (y<=wz) count(s*2,l,wz,x,y);
        else if (x>wz) count(s*2+1,wz+1,r,x,y);
            else
            {
                count(s*2,l,wz,x,wz);
                count(s*2+1,wz+1,r,wz+1,y);
            }
}
void change(int s,int l,int r,int x,int y,int z)
{
    if (l==x && r==y)
    {
        tree[s][0]=z;
        tree[s][1]=(r-l+1)*z;
        tree[s][2]=z;
        return;
    }
    if (tree[s][2]!=-1)
    {
        tree[s*2][0]=tree[s][2];
        tree[s*2][1]=tree[s][2]*(tree[s*2][4]-tree[s*2][3]+1);
        tree[s*2][2]=tree[s][2];
        tree[s*2+1][0]=tree[s][2];
        tree[s*2+1][1]=tree[s][2]*(tree[s*2+1][4]-tree[s*2+1][3]+1);
        tree[s*2+1][2]=tree[s][2];
        tree[s][2]=-1;
    }
    int wz=(l+r)/2;
    if (y<=wz) change(s*2,l,wz,x,y,z);
        else if (x>wz) change(s*2+1,wz+1,r,x,y,z);
            else
            {
                change(s*2,l,wz,x,wz,z);
                change(s*2+1,wz+1,r,wz+1,y,z);
            }
    tree[s][1]=tree[s*2][1]+tree[s*2+1][1];
}
void search(int s,int l,int r,int x)
{
    if (l==r)
    {
        temp=tree[s][0];
        return;
    }
    if (tree[s][2]!=-1)
    {
        tree[s*2][0]=tree[s][2];
        tree[s*2][1]=tree[s][2]*(tree[s*2][4]-tree[s*2][3]+1);
        tree[s*2][2]=tree[s][2];
        tree[s*2+1][0]=tree[s][2];
        tree[s*2+1][1]=tree[s][2]*(tree[s*2+1][4]-tree[s*2+1][3]+1);
        tree[s*2+1][2]=tree[s][2];
        tree[s][2]=-1;
    }
    int wz=(l+r)/2;
    if (x<=wz) search(s*2,l,wz,x);
          else search(s*2+1,wz+1,r,x);
}
bool ok(int mid)
{
    int i;
    build(1,1,n,mid);
    fo(i,1,m)
    {
        k=0;
        count(1,1,n,b[i][1],b[i][2]);
        switch (b[i][0])
        {
            case 0:{
                kk=(b[i][2]-b[i][1]+1)-k;
                if (kk>0)change(1,1,n,b[i][1],b[i][1]+kk-1,0);
                if (b[i][1]+kk<=b[i][2])change(1,1,n,b[i][1]+kk,b[i][2],1);
                break;
            }
            case 1:{
                if (k>0)change(1,1,n,b[i][1],b[i][1]+k-1,1);
                if (b[i][1]+k<=b[i][2])change(1,1,n,b[i][1]+k,b[i][2],0);
                break;
            }
        }
    }
    search(1,1,n,q);
    if (temp==1) return 1; else return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,m) scanf("%d%d%d",&b[i][0],&b[i][1],&b[i][2]);
    scanf("%d",&q);
    int l=1,r=n;
    while (l<r)
    {
        mid=(l+r)/2;
        if (l+1==r)
        {
            if (ok(r)) l=r;
            break;
        }
        if (ok(mid)) l=mid;else r=mid-1;
    }
    printf("%d",l);
}

——2016.7.20

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值