DZY Loves Sorting HDU - 5649 & P2824排序
written on 2022-05-02
这类题最显著的特征是询问只有一个点,并且与相对大小有关。这种时候就可以采用这样的做法:
-
将普通序列问题转化为01问题,以此在一般的线段树(以序列下标为下标)上维护信息获得高效解答。
-
转为01问题,意思就是大于等于某数的写为1,小于的写为0.
-
二分答案,这时的答案显然具有单调性(想想左右两端点1,n)
于是该问题获解
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m,k,a[N];
struct Seg
{
int val[N<<2],add[N<<2];
void build(int p,int l,int r,int k)
{
val[p]=0,add[p]=-1;
if(l==r)
{
if(a[l]<k) val[p]=0;
else val[p]=1;
return ;
}
int mid=l+r>>1;
build(p<<1,l,mid,k),build(p<<1|1,mid+1,r,k);
val[p]=val[p<<1]+val[p<<1|1];
}
void spread(int p,int l,int r)
{
if(add[p]==-1) return ;
int mid=l+r>>1;
add[p<<1]=add[p<<1|1]=add[p];
val[p<<1]=add[p]*(mid-l+1),val[p<<1|1]=add[p]*(r-mid);
add[p]=-1;//..careless
}
void update(int p,int l,int r,int L,int R,int v)
{
if(L<=l&&R>=r)
{
val[p]=v*(r-l+1);
add[p]=v;
return ;
}
spread(p,l,r);
int mid=l+r>>1;
if(L<=mid) update(p<<1,l,mid,L,R,v);
if(R>mid) update(p<<1|1,mid+1,r,L,R,v);
val[p]=val[p<<1]+val[p<<1|1];
}
int ask(int p,int l,int r,int L,int R)
{
if(L<=l&&R>=r) return val[p];
spread(p,l,r);
int mid=l+r>>1,res=0;
if(L<=mid) res+=ask(p<<1,l,mid,L,R);
if(R>mid) res+=ask(p<<1|1,mid+1,r,L,R);
return res;
}
}t1;
struct F{int op,l,r;}q[N];
bool check(int x)
{
t1.build(1,1,n,x);
for(int i=1;i<=m;i++)
{
int op=q[i].op,l=q[i].l,r=q[i].r,num=t1.ask(1,1,n,l,r);
if(l>=r) continue;
if(num==r-l+1||num==0) continue;
if(op==0) t1.update(1,1,n,l,r-num,0),t1.update(1,1,n,r-num+1,r,1);
else t1.update(1,1,n,l,l+num-1,1),t1.update(1,1,n,l+num,r,0);
}
// printf("x=%d num=%d\n",x,t1.ask(1,1,n,1,n));
return t1.ask(1,1,n,k,k)==1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
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",&q[i].op,&q[i].l,&q[i].r);
scanf("%d",&k);
int l=1,r=n,ans;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
}
好像还有一种效率更高的线段树分裂的做法?