【TJOI2016】【bzoj4552】排序(二分答案+线段树01排序)
problem
给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序
排序分为两种
1:(0,l,r)表示将区间[l,r]的数字升序排序
2:(1,l,r)表示将区间[l,r]的数字降序排序
最后询问第q位置上的数字。
solution
考虑二分答案
对于mid
1.将所有 >= mid 的数变成 1, < mid 的数变成 0 (01排序不影响排序结果,得到相对的大小关系)
2.将处理过后的序列进行排序
如果 q 这个位置上最后是 0 ,则 ans < mid ,我们就去找更小的值
如果 q 这个位置上最后是 1, 则 ans >= mid,我们就去找更大的值
这样原题就变成了01序列排序,可以用线段树logn维护
假如说将 [l, r] 这段升序排列,
算出 sum = find(l, r) 即 [l, r] 中 1 的个数
维护排序的操作:
change(r - sum + 1, r, 1); change(l, r - sum, 0);
codes
#include<cstdio>
const int maxn = 100010;
#define lch o<<1
#define rch o<<1|1
int _a[maxn], sgt[maxn<<2], tag[maxn<<2];
void build(int o, int l, int r){
tag[o] = -1;
if(l == r){
sgt[o] = _a[l];
return ;
}
int mid = l+r>>1;
build(lch,l,mid); build(rch,mid+1,r);
sgt[o] = sgt[lch]+sgt[rch];
}
int pushdown(int o, int l, int r){
if(tag[o] != -1){
int mid = l+r>>1;
sgt[lch] = (mid-l+1)*tag[o];
sgt[rch] = (r-mid)*tag[o];
tag[lch] = tag[rch] = tag[o];
tag[o] = -1;
}
}
int query(int o, int l, int r, int L, int R){
if(r < L || l > R)return 0;
if(L <= l && r <= R)return sgt[o];
pushdown(o, l, r);
int mid = l+r>>1, ans = 0;
if(L <= mid)ans += query(lch, l, mid, L, R);
if(R > mid)ans += query(rch, mid+1, r, L, R);
return ans;
}
void change(int o, int l, int r, int L, int R, int v){
if(r < L || l > R)return ;
if(L <= l && r <= R){
sgt[o] = (r-l+1)*v; tag[o] = v;
return ;
}
pushdown(o,l,r);
int mid = l+r>>1;
if(L <= mid)change(lch, l, mid, L, R, v);
if(R > mid)change(rch, mid+1, r, L, R, v);
sgt[o] = sgt[lch]+sgt[rch];
}
int n, m, a[maxn], op[maxn], x[maxn], y[maxn], pos;
int check(int _x){
for(int i = 1; i <= n; i++)
_a[i] = a[i]>=_x;
build(1,1,n);
for(int i = 1; i <= m; i++){
int t = query(1,1,n,x[i],y[i]);
if(op[i])change(1,1,n,x[i],x[i]+t-1,1),change(1,1,n,x[i]+t,y[i],0);
else change(1,1,n,x[i],y[i]-t,0), change(1,1,n,y[i]-t+1,y[i],1);
}
return query(1,1,n,pos,pos);
}
int main(){
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",&op[i],&x[i],&y[i]);
scanf("%d", &pos);
int l = 1, r = n;
while(l < r){
int mid = l+r+1>>1;
if(check(mid))l = mid;
else r = mid-1;
}
printf("%d\n", l);
return 0;
}