数据结构训练之四

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;
}

posted @ 2019-10-27 19:21  wzx_believer  阅读(157)  评论(0编辑  收藏  举报