[HEOI2016/TJOI2016]排序-題解
- 【題目地址】
【本題有O(nlogn)的線段樹分裂合併的做法】但是這裡由於這個方法實現較複雜我才不會告訴你我不會,所以這裡講另外一種十分巧妙的技巧。
我們來看,如果每次對於每個操作直接排序的話,複雜度會達到,而題目只在最後詢問一次,所以我們考慮將操作存下來,離線進行計算。
考慮如何進行快速的對一段區間進行排序:
普通的為肯定承受不了,但是我們只需要知道排序後的第個數是多少,所以我們只用考慮將比答案小的排前面,大的排後面即可,所以我們可以選取一個值作為答案,顯然這個值是有單調性的,所以我們可以將原序列全部排序後在上面二分這個值,而對於二分了的這個值,我們把原序列變成一個01序列,0表示比這個二分值小,1表示大。
那麼繼而考慮如何對於一段01序列排序,其實我們只需知道這一段的01個數,然後如果是升序,就將原來序列的前面0的那麼多個數字變成0,後面的全部變成1即可;降序就是反起來。
那麼用線段樹區間修改查詢,我們可以做到每次的排序,所以每次二分了一個值後,我們就將序列轉換,進行操作,最後判斷第個位置上是0還是1,如果是1,那麼就表示答案還要大一些,否則就小一些。
總的複雜度加上二分,就是,但是不能應對強制線上和多組詢問。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=1e5+10;
int n,m,Q,val[M],bit[M];
int cnt[M<<2],lazy[M<<2];
void pushup(int o){
cnt[o]=cnt[o<<1]+cnt[o<<1|1];
}
void pushdown(int o,int l,int r,int mid){
if(lazy[o]==-1) return;
lazy[o<<1]=lazy[o<<1|1]=lazy[o];
if(!lazy[o]){
cnt[o<<1]=cnt[o<<1|1]=0;
}else{
cnt[o<<1]=(mid-l+1);
cnt[o<<1|1]=(r-mid);
}
lazy[o]=-1;
}
void build(int o,int l,int r){
lazy[o]=-1;
if(l==r){
cnt[o]=(bit[l]==1);
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
int Find_Bit(int o,int l,int r,int L,int R){
if(L<=l&&r<=R) return cnt[o];
if(!cnt[o]) return 0;
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(R<=mid) return Find_Bit(o<<1,l,mid,L,R);
else if(L>mid) return Find_Bit(o<<1|1,mid+1,r,L,R);
else return Find_Bit(o<<1,l,mid,L,R)+Find_Bit(o<<1|1,mid+1,r,L,R);
}
void Update(int o,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
cnt[o]=(v==1)*(r-l+1);
lazy[o]=v;
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) Update(o<<1,l,mid,L,R,v);
if(R>mid) Update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
int Query(int o,int l,int r,int p){
if(l==r) return cnt[o];
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(!cnt[o]) return 0;
if(p<=mid) return Query(o<<1,l,mid,p);
else return Query(o<<1|1,mid+1,r,p);
}
struct Opt{
int opt,l,r;
void in(){scanf("%d%d%d",&opt,&l,&r);}
Opt(){}
Opt(int a,int b,int c):opt(a),l(b),r(c){}
}Op[M];
bool solve(int mv){
for(int i=1;i<=n;i++){
if(val[i]<=mv)bit[i]=0;
else bit[i]=1;
}
build(1,1,n);
for(int i=1,num;i<=m;i++){
num=Find_Bit(1,1,n,Op[i].l,Op[i].r);
if(Op[i].opt){
Update(1,1,n,Op[i].l,Op[i].l+num-1,1);
Update(1,1,n,Op[i].l+num,Op[i].r,0);
}else{
num=Op[i].r-Op[i].l+1-num;
Update(1,1,n,Op[i].l,Op[i].l+num-1,0);
Update(1,1,n,Op[i].l+num,Op[i].r,1);
}
}
return Query(1,1,n,Q);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1;i<=m;i++)Op[i].in();
scanf("%d",&Q);
int l=1,r=n,mid;
while(l<r){
mid=l+r>>1;
if(solve(mid)){
l=mid+1;
}else{
r=mid;
}
}
printf("%d\n",l);
return 0;
}
這裡有個技巧就是對原序列排序時間複雜度過高,但對一個01序列排序則可以大大降低時間複雜度。【[APIO2018]的二分專題講解的課件上還有一些關於二分01序列排序的技巧】
相關文章
- P2824 [HEOI2016/TJOI2016] 排序排序
- 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治優化DP優化
- P2825 [HEOI2016/TJOI2016] 遊戲 與 P10945 Place the Robots遊戲
- 【TJOI2016】【bzoj4552】排序(二分答案+線段樹01排序)排序
- C語言解決排序問題C語言排序
- Public NOIP Round #6 D 排序 題解排序
- 分治思想--快速排序解決TopK問題排序TopK
- 排序專題 -- (1)插入排序排序
- 透過Treeset解決隨機數排序問題隨機排序
- Java介面返回JSON排序無需的問題解決JavaJSON排序
- 2024百度之星決賽部分題解(難度排序前六題)排序
- 堆排序詳解排序
- 圖解快速排序圖解排序
- 「NOI2022 D2T2 氣泡排序」題解排序
- 圖解選擇排序與插入排序圖解排序
- 氣泡排序和選擇排序詳解排序
- LeetCode 熱題 HOT 100 Java題解——33. 搜尋旋轉排序陣列LeetCodeJava排序陣列
- 【排序演算法動畫解】排序介紹及氣泡排序排序演算法動畫
- Java 之 Map 的鍵,值多重排序問題解決方案Java排序
- JavaScript表格排序詳解JavaScript排序
- Java 物件排序詳解Java物件排序
- Array.sort排序問題排序
- 歸併排序加例題排序
- OJ題之氣泡排序排序
- leetcode排序專題演算法刷題LeetCode排序演算法
- Sort排序專題(5)快速排序(QuickSort)(C++實現)排序UIC++
- O(nlogn)快速排序-雙路排序+詳細註解排序
- 第三章:查詢與排序(下)----------- 3.23 相關題解:排序陣列中找和的因子排序陣列
- 第三章:查詢與排序(下)----------- 3.27 用計數排序解決員工年齡問題排序
- Sort排序專題(7)歸併排序(MergeSort)(C++實現)排序C++
- 圖解排序演算法圖解排序演算法
- 氣泡排序與選擇排序超詳細講解排序
- 每天一道演算法題--排序之桶排序實現求排序後相鄰最大差值問題演算法排序
- 彩虹排序 | 荷蘭旗問題排序
- 第三章:查詢與排序(下)----------- 3.29 題解:判斷陣列的包含問題排序陣列
- 【排序演算法動畫解】直接插入排序排序演算法動畫
- JavaScript快速排序功能詳解JavaScript排序
- 遞增排序個人見解排序