1. 程式人生 > >【BZOJ】4552: [Tjoi2016&Heoi2016]排序-二分&線段樹

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

傳送門:bzoj4552


題解

二分答案 m i d mid ,如何判斷 q q 位置上的數是否大於 m

i d mid 呢?

與排序和數值大小有關的問題常用的套路就是把 m i d \geq mid

的數看作1, < m i d <mid 的數看作0。本題依次套路將操作轉成了區間求和&賦值。

複雜度 O

( m l o g 2 n ) O(mlog^2n)


程式碼

#include<bits/stdc++.h>
#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;
}