传送门: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;
}