BZOJ4552 HEOI/TJOI2016 排序 线段树、二分答案

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4552

题意:给出一个$1$到$N$的全排列,对其进行$M$次排序,每次排序将区间$[l,r]$从小到大或从大到小排序,求排序完后位置$q$上的数字。$N,M \leq 10^5$,时限$6s$


名字挂羊头卖狗肉……

暴力排序$O(NMlogN)$神级常数才能过,故考虑在排序上降低复杂度

考虑二分答案,将小于等于当前答案的设为$0$,大于当前答案的设为$1$,这样修改就变成$0,1$排序,可以使用线段树区间覆盖实现

时间复杂度$O(Mlog^2N)$

  1 #include<bits/stdc++.h>
  2 #define MAXN 100005
  3 using namespace std;
  4 inline int read(){
  5     int a = 0;
  6     char c = getchar();
  7     while(!isdigit(c))
  8         c = getchar();
  9     while(isdigit(c)){
 10         a = (a << 3) + (a << 1) + (c ^ '0');
 11         c = getchar();
 12     }
 13     return a;
 14 }
 15 
 16 struct node{
 17     int l , r , mark , num1;
 18 }Tree[MAXN << 4];
 19 int num[MAXN] , N , sortNum[MAXN][3] , M , askNum , mid;
 20 
 21 inline void pushup(int dir){
 22     Tree[dir].num1 = Tree[dir << 1].num1 + Tree[dir << 1 | 1].num1;
 23 }
 24 
 25 inline void pushdown(int dir){
 26     if(Tree[dir].mark == 0)
 27         Tree[dir << 1].mark = Tree[dir << 1 | 1].mark = Tree[dir << 1].num1 = Tree[dir << 1 | 1].num1 = 0;
 28     else
 29         if(Tree[dir].mark == 1){
 30             Tree[dir << 1].mark = Tree[dir << 1 | 1].mark = 1;
 31             Tree[dir << 1].num1 = Tree[dir << 1].r - Tree[dir << 1].l + 1;
 32             Tree[dir << 1 | 1].num1 = Tree[dir << 1 | 1].r - Tree[dir << 1 | 1].l + 1;
 33         }
 34     Tree[dir].mark = -1;
 35 }
 36 
 37 void init(int l , int r , int dir){
 38     Tree[dir].l = l;
 39     Tree[dir].r = r;
 40     Tree[dir].mark = -1;
 41     if(l == r)
 42         Tree[dir].num1 = num[l] > mid;
 43     else{
 44         init(l , l + r >> 1 , dir << 1);
 45         init((l + r >> 1) + 1 , r , dir << 1 | 1);
 46         pushup(dir);
 47     }
 48 }
 49 
 50 void change(int l , int r , int dir , int mark){
 51     if(Tree[dir].l >= l && Tree[dir].r <= r){
 52         Tree[dir].mark = mark;
 53         Tree[dir].num1 = mark * (Tree[dir].r - Tree[dir].l + 1);
 54         return; 
 55     }
 56     pushdown(dir);
 57     if(l <= Tree[dir].l + Tree[dir].r >> 1)
 58         change(l , r , dir << 1 , mark);
 59     if(r > Tree[dir].l + Tree[dir].r >> 1)
 60         change(l , r , dir << 1 | 1 , mark);
 61     pushup(dir);
 62 }
 63 
 64 int ask(int l , int r , int dir){
 65     if(Tree[dir].l >= l && Tree[dir].r <= r)
 66         return Tree[dir].num1;
 67     int sum = 0;
 68     pushdown(dir);
 69     if(l <= Tree[dir].l + Tree[dir].r >> 1)
 70         sum += ask(l , r , dir << 1);
 71     if(r > Tree[dir].l + Tree[dir].r >> 1)
 72         sum += ask(l , r , dir << 1 | 1);
 73     return sum;
 74 }
 75 
 76 inline bool check(){
 77     init(1 , N , 1);
 78     for(register int i = 1 ; i <= M ; i++){
 79         int t = ask(sortNum[i][1] , sortNum[i][2] , 1);
 80         if(sortNum[i][0]){
 81             change(sortNum[i][1] , sortNum[i][1] + t - 1 , 1 , 1);
 82             if(sortNum[i][1] + t <= sortNum[i][2])
 83                 change(sortNum[i][1] + t , sortNum[i][2] , 1 , 0);
 84         }
 85         else{
 86             change(sortNum[i][2] - t + 1,  sortNum[i][2] , 1 , 1);
 87             if(sortNum[i][2] - t >= sortNum[i][1])
 88                 change(sortNum[i][1] , sortNum[i][2] - t , 1 , 0);
 89         }
 90     }
 91     return !ask(askNum , askNum , 1);
 92 }
 93 int main(){
 94     N = read();
 95     M = read();
 96     for(register int i = 1 ; i <= N ; i++)
 97         num[i] = read();
 98     for(register int i = 1 ; i <= M ; i++){
 99         sortNum[i][0] = read();
100         sortNum[i][1] = read();
101         sortNum[i][2] = read();
102     }
103     askNum = read();
104     int l = 1 , r = N;
105     while(l < r){
106         mid = l + r >> 1;
107         if(check())
108             r = mid;
109         else
110             l = mid + 1;
111     }
112     cout << l;
113     return 0;
114 }

 

转载于:https://www.cnblogs.com/Itst/p/9751874.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值