数据结构训练之四
https://www.luogu.org/problem/P2824#submit
做法:
由于将一个普通序列排序很慢,需要nlogn的时间,所以我们试着把它转化为对01序列排序。先来考虑一个简单的问题:
- 如何将一个01序列排序?(logn的复杂度)
- 对于这个问题,我们使用线段树来维护。查询一段区间内的1的个数记为cnt1,如果是升序,就将这段区间的[r-cnt1+1, r]都更改为1,将[l, r-cnt1]更改为0。降序则将[l, l+cnt1-1]更改为1,将[l+cnt, r]更改为0。这样我们就成功地把排序转化为了区间查询和区间修改。
接下来我们来说本题的做法:
这是一个离线的做法。首先二分答案mid。我们把原排列中大于等于mid的数都标记为1,小于mid的都标记为0。然后对于每个操作我们就将01序列排个序。最后如果第p个位子仍是1的话就是可行的。
这个二分成立因为是满足单调性的:可以简单地假设一下,如果你二分的答案是1,那么原序列所有的值都转化为了1,所以最后肯定是true。如果二分一个值成立当且仅当这个位子的值大于等于mid,故如果check返回true,则l = mid+1,否则r = mid-1。
(这题的思想可以借鉴,比较巧妙)
code by wzxbeliever:
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1e5+5;
int n,m,id,ans,mm;
int val[maxn],tree[maxn<<2],tag[maxn<<2];
struct node{
int op,LL,RR;
}Q[maxn];
il void up(int rt){tree[rt]=tree[rt<<1]+tree[rt<<1|1];return;}
il void bulid(int l,int r,int rt){
if(l==r){tree[rt]=val[l]>=mm;tag[rt]=0;return;}
int mid=l+r>>1;
bulid(l,mid,rt<<1);
bulid(mid+1,r,rt<<1|1);
up(rt);tag[rt]=0;//多次记得每次清空 0---->什么也不干 ;1----->赋值为1;2----->赋值为0
return;
}
il void modify(int l,int r,int rt,int p){
if(!p)return;tag[rt]=p;
if(p==1)tree[rt]=r-l+1;
else tree[rt]=0;
}
il void down(int l,int r,int rt){
if(!tag[rt])return;
int mid=l+r>>1;
modify(l,mid,rt<<1,tag[rt]);
modify(mid+1,r,rt<<1|1,tag[rt]);
tag[rt]=0;
}
il int query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R)return tree[rt];
int mid=l+r>>1;
int tot=0;
down(l,r,rt);
if(L<=mid)tot+=query(l,mid,rt<<1,L,R);
if(mid<R)tot+=query(mid+1,r,rt<<1|1,L,R);
return tot;
}
il void upd(int l,int r,int rt,int L,int R,int p){
if(L<=l&&r<=R){modify(l,r,rt,p);return;}
int mid=l+r>>1;
down(l,r,rt);
if(mid>=L)upd(l,mid,rt<<1,L,R,p);
if(mid<R)upd(mid+1,r,rt<<1|1,L,R,p);
up(rt);
}
il int querypoint(int l,int r,int rt,int pos){
if(l==r)return tree[rt];
int mid=l+r>>1;
down(l,r,rt);
if(pos<=mid)return querypoint(l,mid,rt<<1,pos);
else return querypoint(mid+1,r,rt<<1|1,pos);
}
il bool check(){
bulid(1,n,1);
for(ri i=1;i<=m;i++){
int L=Q[i].LL,R=Q[i].RR;
int cnt=query(1,n,1,L,R);
if(cnt==0)continue;//这个句子很关键,不然会越界re4个点
if(Q[i].op==0){
upd(1,n,1,L,R-cnt,2);
upd(1,n,1,R-cnt+1,R,1);
}
else {
upd(1,n,1,L,L+cnt-1,1);
upd(1,n,1,L+cnt,R,2);
}
}
if(querypoint(1,n,1,id)==1)return true;
else return false;
}
int main(){
scanf("%d%d",&n,&m);
for(ri i=1;i<=n;i++)scanf("%d",&val[i]);
for(ri i=1;i<=m;i++)scanf("%d%d%d",&Q[i].op,&Q[i].LL,&Q[i].RR);
scanf("%d",&id);
int l=1,r=n;
while(l<=r){
mm=l+r>>1;
if(check())ans=mm,l=mm+1;
else r=mm-1;
}
printf("%d\n",ans);
return 0;
}