[BZOJ4552][Tjoi2016&Heoi2016]排序 二分答案+线段树

诶看到题目我以为是很神很神的数据结构,直接想放弃了
看到题解以后发现题目确实很神,不过并不是很强的数据结构,做法很巧很棒

直接处理升序和降序是没法儿做的
二分答案x,将序列里面的所有元素分成大于x的和小于等于x的,分别用0,1表示,这样区间升序/降序就能转化成为区间赋值,线段树维护即可

代码几乎没难度,可是考场里要想到这个解法可能不容易

#include <iostream>
#include <cstdio>

#define mid ((l+r)>>1)
#define ls l,mid,t<<1
#define rs mid+1,r,t<<1^1

#define N 100050

using namespace std;


struct Node{ int siz,v;}tr[4*N];
struct Monster{ int t,l,r; }Q[N];
int ag[4*N],ll,rr,c,ret;

int a[N],b[N],n,m,p;
Node operator+(Node p1,Node p2) {
    Node ret;
    ret.siz = p1.siz + p2.siz;
    ret.v = p1.v + p2.v;
    return ret;
}

inline void push_down(int &t) {
    if (ag[t] == -1) return ;
    ag[t<<1] = ag[t<<1^1] = ag[t];
    tr[t<<1].v = tr[t<<1].siz * ( ag[t] ^ 1 );
    tr[t<<1^1].v = tr[t<<1^1].siz * (ag[t] ^ 1);
    ag[t] = -1;
}

Node build(int l,int r,int t) {
    ag[t] = -1;
    return l == r ? tr[t] = (Node){1,b[l]==0} : tr[t] = build(ls) + build(rs);
}

void update(int l,int r,int t) {
    if (l > rr || r < ll) return ;
    if (l >= ll && r <= rr) {
        ag[t] = c;
        tr[t].v = tr[t].siz * (c^1);
        return ;
    }
    push_down(t); update(ls); update(rs);
    tr[t] = tr[t<<1] + tr[t<<1^1];
}

void query(int l,int r,int t) {
    if (l > rr || r < ll) return ;
    if (l >= ll && r <= rr) { ret += tr[t].v; return ; }
    push_down(t); query(ls); query(rs);
    tr[t] = tr[t<<1] + tr[t<<1^1];
}

bool go(int x) {
    for (int i=1;i<=n;i++) b[i] = a[i] > x;
    build(1,n,1);
    for (int _=1;_<=m;_++) {
        ll = Q[_].l , rr = Q[_].r , ret = 0;
        query(1,n,1);

        if (Q[_].t) ret = rr-ll+1 - ret;
        ll = Q[_].l , rr = Q[_].l + ret - 1 , c = Q[_].t;
        update(1,n,1);
        ll = rr+1 , rr = Q[_].r , c = Q[_].t^1;
        update(1,n,1);
    } 
    ll = rr = p , ret = 0;
    query(1,n,1);
    return ret == 1;
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) scanf("%d%d%d",&Q[i].t,&Q[i].l,&Q[i].r);
    scanf("%d",&p);
    int l = 1 , r = n;
    while (l < r) {
        int m = (l + r) >> 1;
        if (go(m)) r = m; else l = m+1;
    }
    printf("%d\n",l);
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值