1. 程式人生 > >[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+線段樹+算法設計策略

[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+線段樹+算法設計策略

spa close problems ref spl blog nod ans img

4552: [Tjoi2016&Heoi2016]排序

Time Limit: 60 Sec Memory Limit: 256 MB
Submit: 1451 Solved: 734
[Submit][Status][Discuss]

Description

在2016年,佳媛姐姐喜歡上了數字序列。因而他經常研究關於序列的一些奇奇怪怪的問題,現在他在研究一個難題 ,需要你來幫助他。這個難題是這樣子的:給出一個1到n的全排列,現在對這個全排列序列進行m次局部排序,排 序分為兩種:1:(0,l,r)表示將區間[l,r]的數字升序排序2:(1,l,r)表示將區間[l,r]的數字降序排序最後詢問第q 位置上的數字。

Input

輸入數據的第一行為兩個整數n和m。n表示序列的長度,m表示局部排序的次數。1 <= n, m <= 10^5第二行為n個整 數,表示1到n的一個全排列。接下來輸入m行,每一行有三個整數op, l, r, op為0代表升序排序,op為1代表降序 排序, l, r 表示排序的區間。最後輸入一個整數q,q表示排序完之後詢問的位置, 1 <= q <= n。1 <= n <= 10^5 ,1 <= m <= 10^5

Output

輸出數據僅有一行,一個整數,表示按照順序將全部的部分排序結束後第q位置上的數字。

Sample Input

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

Sample Output

5

HINT

Source

Analysis

這道題的正解思路簡直清新qwq

第一眼過去肯定就是拿線段樹合並分裂強行模擬

但是正解:元素模糊化+二分

(元素模糊化是我定義出來的,,,= =)

首先這道題拿正常線段樹做的最大難點非常顯然:這個要怎麽排序啊

所以根據正解思路,我們將元素模糊處理成0和1,這樣排序就變成了 統計+區間賦值

怎麽模糊化呢?二分答案 line ,然後對於 ≥line 的設置成 1,否則為 0

這樣,把二分需要用到的 check函數 定義為第 q 個元素的 0/1 狀態

那麽根據我們的定義,這一位肯定是要 ≥line 的(否則就不對勁),以此確定下一步的二分範圍

有點反證法的意味啊,,,

Code

技術分享
  1 #include<cstdio>
  2 #include<iostream>
  3 #define maxn 1000000
  4 #define mid (L+R)/2
  5 #define lc (rt<<1)
  6 #define rc (rt<<1|1)
  7 using namespace std;
  8 
  9 int arr[maxn],line,n,m,qwq;
 10 
 11 struct data{ int op,L,R; }ask[maxn];
 12 
 13 struct node{
 14     int sum,lazy;
 15 }T[maxn*4];
 16 
 17 void maintain(int rt){ T[rt].sum = T[lc].sum+T[rc].sum; }
 18 
 19 void pushdown(int rt,int L,int R){
 20     if(!T[rt].lazy) return;
 21     
 22     T[lc].sum = (T[rt].lazy-1)*(mid-L+1);
 23     T[rc].sum = (T[rt].lazy-1)*(R-mid);
 24     T[lc].lazy = T[rc].lazy = T[rt].lazy;
 25     T[rt].lazy = 0;
 26 }
 27 
 28 void build(int rt,int L,int R){
 29     T[rt].sum = T[rt].lazy = 0;
 30     if(L == R) T[rt].sum = (arr[L]>=line?1:0);
 31     else{
 32         build(lc,L,mid); build(rc,mid+1,R);
 33         maintain(rt);
 34     }
 35 }
 36 
 37 int query(int rt,int L,int R,int qL,int qR){
 38     pushdown(rt,L,R);
 39     if(qL <= L && R <= qR) return T[rt].sum;
 40     else{
 41         int ans = 0;
 42         if(qL <= mid) ans += query(lc,L,mid,qL,qR);
 43         if(qR > mid) ans += query(rc,mid+1,R,qL,qR);
 44         return ans;
 45     }
 46 }
 47 
 48 int query(int rt,int L,int R,int pos){
 49     pushdown(rt,L,R);
 50 //    if(pos < L || R < pos) return -1;
 51     if(L == R) return T[rt].sum;
 52     else{
 53         if(pos <= mid) return query(lc,L,mid,pos);
 54         else return query(rc,mid+1,R,pos);
 55     }
 56 }
 57 
 58 void modify(int rt,int L,int R,int qL,int qR,int val){
 59     pushdown(rt,L,R);
 60     if(qL > qR) return;
 61     if(qL <= L && R <= qR){
 62         T[rt].sum = val*(R-L+1); T[rt].lazy = val+1;
 63     }else{
 64         if(qL <= mid) modify(lc,L,mid,qL,qR,val);
 65         if(qR > mid) modify(rc,mid+1,R,qL,qR,val);
 66         maintain(rt);
 67     }
 68 }
 69 
 70 void SORT(int op,int L,int R){
 71     int sum = query(1,1,n,L,R);
 72 //    printf("#%d: sum%d\n",op,sum);
 73     if(op){
 74         modify(1,1,n,L,L+sum-1,1);
 75         modify(1,1,n,L+sum,R,0);
 76     }else{
 77         modify(1,1,n,L,R-sum,0);
 78         modify(1,1,n,R-sum+1,R,1);
 79     }
 80 }
 81 
 82 bool check(){
 83     build(1,1,n);
 84 //    cout << "Now...."; for(int i = 1;i <= n;i++) printf("%d ",query(1,1,n,i)); cout << endl;
 85     for(int i = 1;i <= m;i++){
 86         SORT(ask[i].op,ask[i].L,ask[i].R);
 87 //        printf("#%d Now..",i); for(int j = 1;j <= n;j++) printf("%d ",query(1,1,n,j)); cout << endl;
 88     }
 89     return query(1,1,n,qwq);
 90 }
 91 
 92 int main(){
 93     scanf("%d%d",&n,&m);
 94     
 95     for(int i = 1;i <= n;i++) scanf("%d",&arr[i]);
 96     for(int i = 1;i <= m;i++) scanf("%d%d%d",&ask[i].op,&ask[i].L,&ask[i].R);
 97     
 98     scanf("%d",&qwq);
 99     
100     int L = 1,R = n;
101     while(L < R){
102         line = (L+R+1)/2;
103         if(!check()) R = line-1;
104         else L = line;
105 //        printf("[%d,%d]: line%d --=",L,R,line);
106 //        for(int i = 1;i <= n;i++) printf("%d ",query(1,1,n,i)); cout << endl;
107     }
108     
109     cout << R;
110     
111     return 0;
112 }
線段樹+二分

[BZOJ] 4552: [Tjoi2016&Heoi2016]排序 #二分+線段樹+算法設計策略