题目大意:
题目链接:
洛谷:https://www.luogu.org/problem/P2824
LOJ:https://loj.ac/problem/2055#submit_code
给出一个
1
∼
n
1\sim n
1∼n的排列,两个操作:
- 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,求出最终位于数列第 q q q位的数字。
思路:
n
b
nb
nb思路!
暴力的复杂度时
O
(
n
m
log
n
)
O(nm\log n)
O(nmlogn)的,其中排序复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn),如果可以有效减少排序的复杂度,就可以降低代码复杂度。
我们发现,01序列的排序可以在
O
(
log
n
)
O(\log n)
O(logn)内完成。以升序排列为例,我们用线段树维护出区间
[
l
,
r
]
[l,r]
[l,r]数字1的个数
s
u
m
sum
sum,然后区间修改
[
l
,
r
−
s
u
m
+
1
]
[l,r-sum+1]
[l,r−sum+1]为1,
[
l
,
l
+
s
u
m
]
[l,l+sum]
[l,l+sum]为0。 .
那么如何把原数列转换成01数列呢?考虑二分答案。
我们在区间
[
1
,
n
]
[1,n]
[1,n]内二分答案
m
i
d
mid
mid,每次把大于等于
m
i
d
mid
mid的数字变成1,小于
m
i
d
mid
mid的数字变成0,然后
O
(
m
log
n
)
O(m\log n)
O(mlogn)完成所有操作,最终如果在第
q
q
q为上的数字是1,那么最终答案一定大于等于
m
i
d
mid
mid,否则小于
m
i
d
mid
mid。
时间复杂度
O
(
m
log
2
n
)
O(m\log^2n)
O(mlog2n)
代码:
#include <cstdio>
#include <string>
#define reg register
using namespace std;
const int N=100010;
int n,m,opt,l,r,mid,x,y,q,a[N],b[N];
struct Ask
{
int l,r,opt;
}ask[N];
struct Tree
{
int l,r,lazy,sum;
}tree[N*4];
int read()
{
int d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void build(int x)
{
tree[x].lazy=-1; tree[x].sum=0;
if (tree[x].l==tree[x].r)
{
tree[x].sum=a[tree[x].l];
return;
}
int mid=(tree[x].l+tree[x].r)>>1;
tree[x*2].l=tree[x].l;
tree[x*2].r=mid;
tree[x*2+1].l=mid+1;
tree[x*2+1].r=tree[x].r;
build(x*2); build(x*2+1);
tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
}
void pushdown(int x)
{
if (tree[x].lazy!=-1)
{
tree[x*2].lazy=tree[x].lazy;
tree[x*2+1].lazy=tree[x].lazy;
tree[x*2].sum=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
tree[x*2+1].sum=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
tree[x].lazy=-1;
}
}
int find(int x,int l,int r)
{
if (l>r) return 0;
if (tree[x].l==l && tree[x].r==r)
return tree[x].sum;
pushdown(x);
int mid=(tree[x].l+tree[x].r)>>1;
if (r<=mid) return find(x*2,l,r);
if (l>mid) return find(x*2+1,l,r);
return find(x*2,l,mid)+find(x*2+1,mid+1,r);
}
void change(int x,int l,int r,int val)
{
if (l>r) return;
if (tree[x].l==l && tree[x].r==r)
{
tree[x].sum=val*(tree[x].r-tree[x].l+1);
tree[x].lazy=val;
return;
}
pushdown(x);
int mid=(tree[x].l+tree[x].r)>>1;
if (r<=mid) change(x*2,l,r,val);
else if (l>mid) change(x*2+1,l,r,val);
else change(x*2,l,mid,val),change(x*2+1,mid+1,r,val);
tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
}
int main()
{
n=read(); m=read();
for (reg int i=1;i<=n;i++)
b[i]=read();
for (reg int i=1;i<=m;i++)
ask[i].opt=read(),ask[i].l=read(),ask[i].r=read();
q=read();
l=1; r=n;
while (l<=r)
{
mid=(l+r)>>1;
for (reg int i=1;i<=n;i++)
if (b[i]<mid) a[i]=0;
else a[i]=1;
tree[1].l=1; tree[1].r=n;
build(1);
for (reg int i=1;i<=m;i++)
{
int sum=find(1,ask[i].l,ask[i].r);
if (!ask[i].opt)
{
change(1,ask[i].l,ask[i].r-sum,0);
change(1,ask[i].r-sum+1,ask[i].r,1);
}
else
{
change(1,ask[i].l,ask[i].l+sum-1,1);
change(1,ask[i].l+sum,ask[i].r,0);
}
}
if (find(1,q,q)==1) l=mid+1;
else r=mid-1;
}
printf("%d\n",l-1);
return 0;
}