洛 谷 P 2824 [ H E O I 2016 / T J O I 2016 ] 排 序 \color{#00F}{洛谷\ P2824\ [HEOI2016/TJOI2016]排序} 洛谷 P2824 [HEOI2016/TJOI2016]排序
T
i
m
e
L
i
m
i
t
:
6.00
S
\color{green}{Time\ Limit: 6.00S}
Time Limit:6.00S
M
e
m
o
r
y
L
i
m
i
t
:
250.00
M
B
\color{green}{Memory\ Limit: 250.00MB}
Memory Limit:250.00MB
P
r
o
b
l
e
m
D
e
s
c
r
i
p
t
i
o
n
\color{blue}{Problem\ Description}
Problem Description
洛谷 P2824 [HEOI2016/TJOI2016]排序
在
2016
2016
2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个
1
1
1到
n
n
n的全排列,现在对这个全排列序列进行
m
m
m次局部排序,排序分为两种:
- ( 0 , l , r ) (0,l,r) (0,l,r)表示将区间 [ l , r ] [l,r] [l,r]的数字升序排序
- ( 1 , l , r ) (1,l,r) (1,l,r)表示将区间 [ l , r ] [l,r] [l,r]的数字降序排序
最后询问第 q q q位置上的数字。
I
n
p
u
t
\color{blue}{Input}
Input
输入数据的第一行为两个整数
n
n
n和
m
m
m。
n
n
n表示序列的长度,
m
m
m表示局部排序的次数。第二行为
n
n
n个整数,表示
1
1
1到
n
n
n的一个全排列。接下来输入
m
m
m行,每一行有三个整数
o
p
op
op,
l
l
l,
r
r
r,
o
p
op
op为
0
0
0代表升序排序,
o
p
op
op为
1
1
1代表降序排序,
l
l
l,
r
r
r表示排序的区间。最后输入一个整数
q
q
q,
q
q
q表示排序完之后询问的位置。
O
u
t
p
u
t
\color{blue}{Output}
Output
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第
q
q
q位置上的数字。
S
a
m
p
l
e
I
n
p
u
t
\color{blue}{Sample\ Input}
Sample Input
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
S
a
m
p
l
e
O
u
t
p
u
t
\color{blue}{Sample \ Output}
Sample Output
5
由于将一个普通序列排序很慢,需要 n l o g n nlogn nlogn的时间,所以我们试着把它转化为对 01 01 01序列排序。先来考虑一个简单的问题:
如何将一个 01 01 01序列排序?( l o g n logn logn的复杂度)
对于这个问题,我们使用线段树来维护。查询一段区间内的 1 1 1的个数记为 c n t cnt cnt,如果是升序,就将这段区间的 [ r − c n t + 1 , r ] [r-cnt+1, r] [r−cnt+1,r]都更改为 1 1 1,将 [ l , r − c n t ] [l, r-cnt] [l,r−cnt]更改为 0 0 0。降序则将 [ l , l + c n t − 1 ] [l, l+cnt-1] [l,l+cnt−1]更改为 1 1 1,将 [ l + c n t , r ] [l+cnt, r] [l+cnt,r]更改为 0 0 0。这样我们就成功地把排序转化为了区间查询和区间修改。
接下来我们来说本题的做法:
这是一个离线的做法。首先二分答案 m i d mid mid。我们把原排列中大于等于 m i d mid mid的数都标记为 1 1 1,小于 m i d mid mid的都标记为 0 0 0。然后对于每个操作我们就将 01 01 01序列排个序。最后如果第 p p p个位子仍是 1 1 1的话就是可行的。
这个二分成立因为是满足单调性的:可以简单地假设一下,如果你二分的答案是 1 1 1,那么原序列所有的值都转化为了 1 1 1,所以最后肯定是 t r u e true true。如果二分一个值成立当且仅当这个位子的值大于等于 m i d mid mid,故如果 c h e c k check check返回 t r u e true true,则 l = m i d + 1 l = mid+1 l=mid+1并且 a n s = m i d ans=mid ans=mid,否则 r = m i d − 1 r = mid-1 r=mid−1。
代码实现如下:
#include <bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int MAXN=1e5+5;
int n,m,q;
int a[MAXN],sum[MAXN<<2],lazy[MAXN<<2];
struct Query
{
int op;
int l,r;
}que[MAXN];
inline int read()
{
int X=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+(ch^'0'); ch=getchar();}
return X*f;
}
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int l,int r,int rt)
{
if(lazy[rt]!=-1)
{
int mid=(l+r)>>1;
sum[rt<<1]=(mid-l+1)*lazy[rt];
lazy[rt<<1]=lazy[rt];
sum[rt<<1|1]=(r-mid)*lazy[rt];
lazy[rt<<1|1]=lazy[rt];
lazy[rt]=-1;
}
}
void buildtree(int m,int l,int r,int rt)
{
lazy[rt]=-1;
if(l==r)
{
sum[rt]=a[l]>=m;
return;
}
int mid=(l+r)>>1;
buildtree(m,lson);
buildtree(m,rson);
pushup(rt);
}
void updata(int L,int R,int k,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
sum[rt]=(r-l+1)*k;
lazy[rt]=k;
return;
}
pushdown(l,r,rt);
int mid=(l+r)>>1;
if(L<=mid) updata(L,R,k,lson);
if(mid+1<=R) updata(L,R,k,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return sum[rt];
pushdown(l,r,rt);
int mid=(l+r)>>1,ans=0;
if(L<=mid) ans+=query(L,R,lson);
if(mid+1<=R) ans+=query(L,R,rson);
return ans;
}
int queryPoint(int x,int l,int r,int rt)
{
if(x==l&&r==x) return sum[rt];
pushdown(l,r,rt);
int mid=(l+r)>>1;
if(x<=mid) return queryPoint(x,lson);
if(mid+1<=x) return queryPoint(x,rson);
}
bool check(int mid)
{
buildtree(mid,1,n,1);
for(int i=1;i<=m;++i)
{
int cnt=query(que[i].l,que[i].r,1,n,1);
if(que[i].op==0)
{//当区间[l,r]中全部为1时,可能会出现que[i].l>que[i].r-cnt的情况,其他三种同理
if(que[i].l<=que[i].r-cnt) updata(que[i].l,que[i].r-cnt,0,1,n,1);
if(que[i].r-cnt+1<=que[i].r) updata(que[i].r-cnt+1,que[i].r,1,1,n,1);
}
else if(que[i].op==1)
{
if(que[i].l<=que[i].l+cnt-1) updata(que[i].l,que[i].l+cnt-1,1,1,n,1);
if(que[i].l+cnt<=que[i].r) updata(que[i].l+cnt,que[i].r,0,1,n,1);
}
}
return queryPoint(q,1,n,1);
}
void readdata()
{
n=read(); m=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=m;++i)
que[i].op=read(),que[i].l=read(),que[i].r=read();
q=read();
}
void work()
{
int l=1,r=n,ans;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d",ans);
}
int main()
{
// freopen("input.txt","r",stdin);
readdata();
work();
return 0;
}