【HEOI2016/TJOI2016】排序(二份答案+线段树)
一道思路很好的题
首先考虑如果是0/1串该怎么做
我们发现我们可以在的时间完成对这样一个序列的排序
先出这个区间的和
那么显然所有的都到一边去了到另一边去了
那么这个区间就都是,剩下的都是1,那我们打一个区间覆盖标记就可以了
考虑到题目中给的是一个~的全排列
所以我们可以直接二分这个位置是什么数
那么把整个序列中大于他的设为1,小于的设为0
那么就变成了上面说的0/1串问题
暴力做一遍之后看这个位置上是0还是1
0就把上界调低,1就把下界调高
正确的就不用证明了吧
结果半天过不了发现二分和线段树宏冲突了
代码:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
#define lc (u<<1)
#define rc ((u<<1)+1)
#define mid ((l+r)>>1)
const int N=100005;
int op[N],L[N],k,R[N],val[N],tr[N<<2],mark[N<<2],n,m,a[N];
inline void pushup(int u){tr[u]=tr[lc]+tr[rc];}
inline void buildtree(int u,int l,int r){
mark[u]=-1;
if(l==r){tr[u]=a[l];return;}
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
pushup(u);
}
inline void pushdown(int u,int p,int t){
if(mark[u]==-1)return;
mark[lc]=mark[rc]=mark[u];
tr[lc]=mark[u]*p,tr[rc]=mark[u]*t;
mark[u]=-1;
}
inline void update(int u,int l,int r,int st,int des,int k){
if(des<l||r<st)return;
if(l>=st&&r<=des){
tr[u]=k*(r-l+1);
mark[u]=k;return;
}
pushdown(u,mid-l+1,r-mid);
update(lc,l,mid,st,des,k);
update(rc,mid+1,r,st,des,k);
pushup(u);
}
inline int query(int u,int l,int r,int st,int des){
if(l>des||r<st)return 0;
if(st<=l&&r<=des)return tr[u];
pushdown(u,mid-l+1,r-mid);
int ans=0;
ans+=query(lc,l,mid,st,des);
ans+=query(rc,mid+1,r,st,des);
return ans;
}
inline bool check(int v){
for(int i=1;i<=n;i++){
if(val[i]>=v)a[i]=1;
else a[i]=0;
}
buildtree(1,1,n);
for(int i=1;i<=m;i++){
int l=L[i],r=R[i];
if(op[i]){
int tmp=query(1,1,n,l,r);
update(1,1,n,l,l+tmp-1,1);
update(1,1,n,l+tmp,r,0);
}
else{
int tmp=query(1,1,n,l,r);
update(1,1,n,l,r-tmp,0);
update(1,1,n,r-tmp+1,r,1);
}
}
return query(1,1,n,k,k);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)val[i]=read();
for(int i=1;i<=m;i++)op[i]=read(),L[i]=read(),R[i]=read();
k=read();
int A=1,B=n,ans=0;
while(A<=B){
int py=(A+B)>>1;
if(check(py))A=py+1,ans=py;
else B=py-1;
}
cout<<ans;
}