【BZOJ4552】排序(TJOI&HEOI2016)-二分答案+线段树
测试地址:排序
做法:本题需要用到二分答案+线段树。
看到在序列上做一些花里胡哨的操作,我们就想到用线段树,但是……这种操作好像根本做不了啊……
这时我们发现了此题的一个重要条件:询问只有一次,并且在所有修改操作之后。也就是说,我们也许可以找到一个方法进行离线询问。
这时候我们要转化问题。考虑最后的序列,我们把整个序列看做是按照数字从大到小一个一个插入到对应位置中去的,那么我们要求的位置上第一次被放进去数时,这个数就是答案。我们发现这个位置上有没有数这个东西是单调的,即在某一个时刻之前都没有数,而在之后都有数,所以我们考虑二分答案来找出这个时刻,那么就转化成了判定性问题:如何判断在进行次排序操作后,某一个位置上有没有一个的数?
我们把所有数分成两类,一类是的,一类是的,我们知道第二类数一定大于第一类数,所以我们用和分别表示这两类数,问题就转化成维护一个序列,支持排序操作。对序列排序可以分为三步:询问区间中的个数,修改一个连续段内的数字为,修改另一个连续段内的数字为。显然这些操作都可以用线段树来维护。那么我们就解决了这个问题,时间复杂度为。
(这道题出的真的贼好,吹爆出题人)
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,q,x,a[100010],op[100010],L[100010],R[100010];
int seg[400010],p[400010];
void pushdown(int no,int l,int r)
{
int mid=(l+r)>>1;
if (p[no]!=-1)
{
p[no<<1]=p[no<<1|1]=p[no];
if (!p[no])
{
seg[no<<1]=mid-l+1;
seg[no<<1|1]=r-mid;
}
else seg[no<<1]=seg[no<<1|1]=0;
p[no]=-1;
}
}
void pushup(int no)
{
seg[no]=seg[no<<1]+seg[no<<1|1];
}
void buildtree(int no,int l,int r)
{
p[no]=-1;
if (l==r)
{
if (a[l]>=x) seg[no]=0;
else seg[no]=1;
return;
}
int mid=(l+r)>>1;
buildtree(no<<1,l,mid);
buildtree(no<<1|1,mid+1,r);
pushup(no);
}
void modify(int no,int l,int r,int s,int t,int type)
{
if (s>t) return;
if (l>=s&&r<=t)
{
p[no]=type;
if (!type) seg[no]=r-l+1;
else seg[no]=0;
return;
}
int mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) modify(no<<1,l,mid,s,t,type);
if (t>mid) modify(no<<1|1,mid+1,r,s,t,type);
pushup(no);
}
int query(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t) return seg[no];
int mid=(l+r)>>1,sum=0;
pushdown(no,l,r);
if (s<=mid) sum+=query(no<<1,l,mid,s,t);
if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
return sum;
}
bool check()
{
buildtree(1,1,n);
for(int i=1;i<=m;i++)
{
int tot;
tot=query(1,1,n,L[i],R[i]);
if (!op[i])
{
modify(1,1,n,L[i],L[i]+tot-1,0);
modify(1,1,n,L[i]+tot,R[i],1);
}
else
{
modify(1,1,n,R[i]-tot+1,R[i],0);
modify(1,1,n,L[i],R[i]-tot,1);
}
}
return !query(1,1,n,q,q);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&op[i],&L[i],&R[i]);
scanf("%d",&q);
int l=1,r=n;
while(l<r)
{
x=((l+r)>>1)+1;
if (check()) l=x;
else r=x-1;
}
printf("%d",l);
return 0;
}