原题链接
解析
“这个题不是典爆了,,,只跟大小相关的题想不到 0/1 Trick 建议先多做题。”
收到。
二分答案 \(x\),将大于等于 \(x\) 的数都标记为 \(1\),小于 \(x\) 的数都标记为 \(0\)。这样排序操作就变成了对 \(0/1\) 串排序,而这个操作相当于统计区间 \(1\) 个数和区间赋值,可以用线段树来维护。最终答案就为使得所有操作过后位置 \(q\) 上的数为 \(1\) 的最小 \(x\)。
时间复杂度 \(O(m\log^2n)\)。
代码
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 2e5 + 5,mod = 998244353;
int a[N],b[N];
int cnt1[N << 2],tag[N << 2];
int pos;
struct Query{int op,l,r;
}q[N];
int n,m;
void push_up(int p){cnt1[p] = cnt1[ls(p)] + cnt1[rs(p)];
}
void build(int p,int l,int r){tag[p] = -1;if(l == r){cnt1[p] = b[l];return;}build(ls(p),l,mid),build(rs(p),mid + 1,r);push_up(p);
}
void add_tag(int p,int l,int r,int k){tag[p] = k;cnt1[p] = (r - l + 1) * k;
}
void push_down(int p,int l,int r){if(tag[p] == -1) return;add_tag(ls(p),l,mid,tag[p]);add_tag(rs(p),mid + 1,r,tag[p]); tag[p] = -1;
}
void modi(int p,int l,int r,int L,int R,int k){if(l > R || r < L) return;if(l >= L && r <= R){add_tag(p,l,r,k);return;}push_down(p,l,r);modi(ls(p),l,mid,L,R,k),modi(rs(p),mid + 1,r,L,R,k);push_up(p);
}
int ask(int p,int l,int r,int L,int R){if(l > R || r < L) return 0;if(l >= L && r <= R){return cnt1[p];}push_down(p,l,r);return ask(ls(p),l,mid,L,R) + ask(rs(p),mid + 1,r,L,R);
}bool chk(int x){for(int i=1;i<=n;i++){b[i] = a[i] >= x;} build(1,1,n);for(int i=1;i<=m;i++){int l = q[i].l,r = q[i].r;int x = ask(1,1,n,l,r);if(q[i].op == 0){modi(1,1,n,l,r - x,0);modi(1,1,n,r - x + 1,r,1);}else{modi(1,1,n,l,l + x - 1,1);modi(1,1,n,l + x,r,0);}}return ask(1,1,n,pos,pos);
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=m;i++){cin>>q[i].op>>q[i].l>>q[i].r;} cin>>pos;int l = 1,r = n;while(l < r){int md = (l + r + 1) >> 1;if(chk(md)){l = md;}else{r = md - 1;}}cout<<l;return 0;
}