【HEOI2016/TJOI2016】排序(二份答案+線段樹)
阿新 • • 發佈:2018-12-31
一道思路很好的題
首先考慮如果是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;
}