您的位置:首页 > 其它

[TJOI&HEOI2016]排序

2016-07-11 14:45 567 查看

题目大意

有一个n的排列,进行m次操作,每次操作是将一个区间升序或降序排序。

请你输出m次操作后第p个位置的值。

数据范围

n,m≤100000

分析

直接排序肯定不好做。

但是如果考虑二分答案,那么问题就变成判断最后第p个位置的值是否小于等于一个数了。

所以对于当前二分的答案mid,把a[i]≤mid的都赋值为1,a[i]>mid的赋为0。以升序排序为例,求出区间有多少个1,假设共有cnt个,那么把这些1放在区间左边cnt个位置,右边的位置全部放0。这部分用线段树解决。

时间复杂度O(nlog2n)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=100005,maxt=262205;

int n,m,a[maxn],q,l,r,mid,L[maxn],R[maxn],typ[maxn],t[maxt],mask[maxt];

void insert(int l,int r,int a,int b,int v,int x)
{
if (l==a && r==b)
{
mask[x]=v;
t[x]=(r-l+1)*mask[x];
return;
}
int mid=(l+r)/2;
if (mask[x]>=0)
{
mask[x*2]=mask[x]; mask[x*2+1]=mask[x];
t[x*2]=mask[x]*(mid-l+1); t[x*2+1]=mask[x]*(r-mid);
mask[x]=-1;
}
if (b<=mid) insert(l,mid,a,b,v,x*2);
else if (a>mid) insert(mid+1,r,a,b,v,x*2+1);
else
{
insert(l,mid,a,mid,v,x*2); insert(mid+1,r,mid+1,b,v,x*2+1);
}
t[x]=t[x*2]+t[x*2+1];
}

int getsum(int l,int r,int a,int b,int x)
{
if (l==a && r==b) return t[x];
int mid=(l+r)/2;
if (mask[x]>=0)
{
mask[x*2]=mask[x]; mask[x*2+1]=mask[x];
t[x*2]=mask[x]*(mid-l+1); t[x*2+1]=mask[x]*(r-mid);
mask[x]=-1;
}
if (b<=mid) return getsum(l,mid,a,b,x*2);
if (a>mid) return getsum(mid+1,r,a,b,x*2+1);
return getsum(l,mid,a,mid,x*2)+getsum(mid+1,r,mid+1,b,x*2+1);
}

int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=0;i<m;i++) scanf("%d%d%d",&typ[i],&L[i],&R[i]);
scanf("%d",&q);
for (l=1,r=n,mid=(l+r)/2;l<r;mid=(l+r)/2)
{
memset(t,0,sizeof(t));
memset(mask,255,sizeof(mask));
for (int i=1;i<=n;i++) insert(1,n,i,i,(a[i]<=mid),1);
for (int i=0;i<m;i++)
{
int cnt=getsum(1,n,L[i],R[i],1);
if (!typ[i])
{
if (cnt) insert(1,n,L[i],L[i]+cnt-1,1,1);
if (cnt<R[i]-L[i]+1) insert(1,n,L[i]+cnt,R[i],0,1);
}else
{
if (cnt) insert(1,n,R[i]-cnt+1,R[i],1,1);
if (cnt<R[i]-L[i]+1) insert(1,n,L[i],R[i]-cnt,0,1);
}
}
if (getsum(1,n,q,q,1)) r=mid;else l=mid+1;
}
printf("%d\n",l);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: