P2824 [HEOI2016/TJOI2016] 排序
简要题意
给定一个长度为 \(n\) 的排列 \(a\),有 \(m\) 次操作:
- 将 \([l,r]\) 从小到大排序
- 将 \([l,r]\) 从大到小排序
求 \(m\) 次操作后 \(a_q\) 的值。
\(n,m\leq 10^5\)
思路
首先这种排序的数据结构没有什么想法,根本原因是因为值太多了。但是我们观察到这是一个排列,这对于解这道题目有什么帮助呢?
排列有两个性质:
- \(max(a_i)=n\),即值域在 \(n\) 内。
- 元素互不相同
如果能利用 \(1\) 性质,去枚举最终的答案并且快速判断就好了。之后考虑如果答案是确定的,那么这么多元素都是没有必要的了。我们可以抽象成 \(\geq ans\) 的元素设为 \(1\) 与 \(<ans\) 的元素设为 \(0\)。
之后排序的操作可以简化成 \(\log_2n\) 级别的了。考虑 \([l,r]\) 中有几个 \(1\),然后把它们都放到最前或最后,用线段树维护即可。但是这仍然是 \(O(nm\log_2n)\) 的,与暴力没有区别。
但是观察到元素互不相同,也就是说明答案唯一。之后发现答案具有单调性。因为如果有 \(\geq ans\) 的元素在 \(q\) 上,那么就可以接着向上找。二分即可。
时间复杂度 \(O(m\log_2^2 n)\)。
代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll MAXN=1e5+5;
struct Query{
ll op,l,r;
}q[MAXN];
struct node{
ll val,tag;
}t[MAXN*4];
ll lc(ll u){
return u<<1;
}
ll rc(ll u){
return u<<1|1;
}
void push_up(ll u){
t[u].val=t[lc(u)].val+t[rc(u)].val;
}
ll a[MAXN],b[MAXN];
void build(ll u,ll l,ll r){
t[u].tag=-1;
if(l==r){
t[u].val=b[l];
return;
}
ll mid=(l+r)>>1;
build(lc(u),l,mid);
build(rc(u),mid+1,r);
push_up(u);
}
void push_down(ll u,ll l,ll r){
if(t[u].tag==-1){
return;
}
ll mid=(l+r)>>1;
t[lc(u)].tag=t[u].tag;
t[lc(u)].val=t[u].tag*(mid-l+1);
t[rc(u)].tag=t[u].tag;
t[rc(u)].val=t[u].tag*(r-(mid+1)+1);
t[u].tag=-1;
}
void add(ll u,ll l,ll r,ll ql,ll qr,ll val){
if(ql<=l&&r<=qr){
t[u].tag=val;
t[u].val=(r-l+1)*val;
return;
}
push_down(u,l,r);
ll mid=(l+r)>>1;
if(ql<=mid){
add(lc(u),l,mid,ql,qr,val);
}
if(mid+1<=qr){
add(rc(u),mid+1,r,ql,qr,val);
}
push_up(u);
}
ll query(ll u,ll l,ll r,ll ql,ll qr){
if(ql<=l&&r<=qr){
return t[u].val;
}
push_down(u,l,r);
ll mid=(l+r)>>1,ans=0;
if(ql<=mid){
ans+=query(lc(u),l,mid,ql,qr);
}
if(mid+1<=qr){
ans+=query(rc(u),mid+1,r,ql,qr);
}
return ans;
}
ll n,m,Q;
bool check(ll x){
for(int i=1;i<=n;++i){
b[i]=a[i]>=x;
}
build(1,1,n);
for(int i=1;i<=m;++i){
ll op=q[i].op,l=q[i].l,r=q[i].r;
ll cnt=query(1,1,n,l,r);
if(cnt==0||cnt==r-l+1){
continue;
}
if(!op){
add(1,1,n,r-cnt+1,r,1);
add(1,1,n,l,r-cnt,0);
}else{
add(1,1,n,l,l+cnt-1,1);
add(1,1,n,l+cnt,r,0);
}
}
return query(1,1,n,Q,Q);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=m;++i){
cin>>q[i].op>>q[i].l>>q[i].r;
}
cin>>Q;
ll l=1,r=n,ans=0;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid)){
l=mid+1;
ans=mid;
}else{
r=mid-1;
}
}
cout<<ans<<endl;
return 0;
}