【BZOJ】4552: [Tjoi2016&Heoi2016]排序-二分&线段树

传送门:bzoj4552


题解

二分答案 m i d mid mid,如何判断 q q q位置上的数是否大于 m i d mid mid呢?

与排序和数值大小有关的问题常用的套路就是把 ≥ m i d \geq mid mid的数看作1, < m i d <mid <mid的数看作0。本题依次套路将操作转成了区间求和&赋值。

复杂度 O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)


代码

#include
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=1e5+10;

int n,m,ini[N],ans,l,r,pos,a[N];
int ss[N<<2],st[N<<2];

struct qr{int op,l,r;}q[N];

void build(int k,int l,int r)
{
     st[k]=-1;if(l==r) {ss[k]=a[l];return;}
     build(lc,l,mid);build(rc,mid+1,r);
     ss[k]=ss[lc]+ss[rc];
}

inline void pushdown(int k,int l,int r)
{
	if(st[k]==-1) return;
	st[lc]=st[rc]=st[k];
    ss[lc]=(mid-l+1)*st[k];ss[rc]=(r-mid)*st[k];
    st[k]=-1;
}

int ask(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return ss[k];
	pushdown(k,l,r);
	if(R<=mid) return ask(lc,l,mid,L,R);
	if(L>mid) return ask(rc,mid+1,r,L,R);
	return ask(lc,l,mid,L,R)+ask(rc,mid+1,r,L,R);
}

void dn(int k,int l,int r)
{
	if(l==r) {a[l]=ss[k];return;}
	pushdown(k,l,r);dn(lc,l,mid);dn(rc,mid+1,r);
}

void cg(int k,int l,int r,int L,int R,int vv)
{
	if(L>R) return;
	if(L<=l && r<=R) {st[k]=vv;ss[k]=(r-l+1)*vv;return;}
	pushdown(k,l,r);
	if(L<=mid) cg(lc,l,mid,L,R,vv);
	if(R>mid) cg(rc,mid+1,r,L,R,vv);
	ss[k]=ss[lc]+ss[rc];
}

inline bool ck(int x)
{
	int i,j,l,r;
	for(i=1;i<=n;++i) a[i]=(ini[i]>=x);
	build(1,1,n);
	for(i=1;i<=m;++i){
		l=q[i].l;r=q[i].r;
		j=ask(1,1,n,l,r);
		if(!q[i].op) cg(1,1,n,l,r-j,0),cg(1,1,n,r-j+1,r,1);
		else cg(1,1,n,l,l+j-1,1),cg(1,1,n,l+j,r,0);
	}
	dn(1,1,n);
	return a[pos]==1;
}

int main(){
    int i,j,l,r;
	scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i) scanf("%d",&ini[i]);
    for(i=1;i<=m;++i) scanf("%d%d%d",&q[i].op,&q[i].l,&q[i].r);
    scanf("%d",&pos);
    for(l=1,r=n;l<=r;) ck(mid)?l=(ans=mid)+1:r=mid-1;
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(线段树,二分)