题目传送门:https://www.luogu.org/problemnew/show/P2839

题目大意:给出一个长度为$N$的序列与$Q$次询问,每次询问左端点在$[a,b]$,右端点在$[c,d]$的区间中最大的中位数,强制在线(本题中的中位数定义与平常不同,设某区间长度为$L$,则在从小到大排序后的序列中(编号从$0$开始),其中位数为第$\lfloor L/2 \rfloor$号元素)$N,Q \leq 2 \times 10^4$


这鬼题让我知道主席树可以用于除第$K$大以外的问题$qwq$

观察$100 \%$的数据规模,$O(nQ)$的做法都比较吃力,所以考虑使用$log$数据结构进行维护获得$O(Qlogn)$或者$O(Qlog^2n)$的算法。故考虑到使用线段树进行维护,同时使用二分的方式寻找每个询问的答案,

其中check的内容就是寻找是否有满足该询问条件的区间,在其中(大于等于当前二分的数的数字个数)要大于等于(小于当前二分的数的数字个数)。断句略奇怪
不妨将大于等于当前二分的数的数字的权值设为1,小于当前二分的数的数字的权值设为-1,check的内容就等价于询问$$max(\sum_{i=x}^y w_i) \geq 0 (x \in {[a , b]} , y \in{[c , d]})$$是否成立。

所以想到对于每个数字建立一个线段树存储权值,在每一次二分询问时求出对应线段树中$x \in {[a , b]} , y \in{[c , d]},\sum_{i=b+1}^{c-1} w_i + max(\sum_{i=x}^b w_i)+max(max(\sum_{i=c}^y w_i))$是否大于0,刚好这三个式子对应区间和、区间最大后缀、区间最大前缀,可以使用线段树解决。
然后发现对于排序后的相邻两数只有一个$1$变成$-1$,就可以使用主席树将空间压到允许范围内了
时间复杂度为$O(Qlog^2n)$,空间复杂度为$O(nlogn)$,符合本题数据范围

 #include<bits/stdc++.h>
 #define MAXN 100002
 using namespace std;
 inline int read(){
     ;
     ;
     char c = getchar();
     while(!isdigit(c)){
         if(c == '-')
             f = ;
         c = getchar();
     }
     while(isdigit(c)){
         a = (a << ) + (a << ) + (c ^ ');
         c = getchar();
     }
     return f ? -a : a;
 }
 ];
 inline void print(int x){
     ;
     )
         fwrite( , stdout);
     else{
         ){
             x = -x;
             fwrite( , stdout);
         }
         while(x){
                output[--dirN] = x %  + ;
             x /= ;
         }
         fwrite(output + dirN ,  , strlen(output + dirN) , stdout);
     }
     fwrite( ,  , stdout);
 }
 struct node{
     int sum , lMax , rMax , l , r;
 }Tree[ * MAXN];
 struct sortNum{//用于排序
     int ind , num;
     bool operator <(sortNum a){
         return num < a.num;
     }
 }sorted[MAXN];
 int num[MAXN] , root[MAXN];
  , rMax , rSum , lMax , lSum;

 inline int max(int a , int b){
     return a > b ? a : b;
 }

 inline void swap(int &a , int &b){
     int t = a;
     a = b;
     b = t;
 }

 //初始化一个所有叶子结点权值都为1的线段树
 void init(int dir , int l , int r){
     Tree[dir].sum = Tree[dir].lMax = Tree[dir].rMax = r - l + ;
     if(l != r){
         init(Tree[dir].l = ++cntNode , l , l + r >> );
         init(Tree[dir].r = ++cntNode , (l + r >> ) +  , r);
     }
 }

 inline void pushup(int dir){
     Tree[dir].lMax = max(Tree[Tree[dir].l].lMax , Tree[Tree[dir].l].sum + Tree[Tree[dir].r].lMax);
     Tree[dir].rMax = max(Tree[Tree[dir].r].rMax , Tree[Tree[dir].r].sum + Tree[Tree[dir].l].rMax);
     Tree[dir].sum = Tree[Tree[dir].l].sum + Tree[Tree[dir].r].sum;
 }

 //更新版本
 void update(int now , int last , int l , int r , int dir){
     if(l == r){
         Tree[now].lMax = Tree[now].rMax = ;
         Tree[now].sum = -;
     }
     else{
         ){
             Tree[now].l = Tree[last].l;
             update(Tree[now].r = ++cntNode , Tree[last].r , (l + r >> ) +  , r , dir);
         }
         else{
             Tree[now].r = Tree[last].r;
             update(Tree[now].l = ++cntNode , Tree[last].l , l , l + r >>  , dir);
         }
         pushup(now);
     }
 }

 //区间和
 int findSum(int dir , int l , int r , int L , int R){
     if(L >= l && R <= r)
         return Tree[dir].sum;
     ;
     )
         sum += findSum(Tree[dir].l , l , r , L , L + R >> );
     )
         sum += findSum(Tree[dir].r , l , r , (L + R >> ) +  , R);
     return sum;
 }

 //区间最大后缀
 void findRightMax(int dir , int l , int r , int L , int R){
     if(L >= l && R <= r){
         rMax = max(rMax , Tree[dir].rMax + rSum);
         rSum += Tree[dir].sum;
         return;
     }
     )
         findRightMax(Tree[dir].r , l , r , (L + R >> ) +  , R);
     )
         findRightMax(Tree[dir].l , l , r , L , L + R >> );
 }

 //区间最大前缀
 void findLeftMax(int dir , int l , int r , int L , int R){
     if(L >= l && R <= r){
         lMax = max(lMax , Tree[dir].lMax + lSum);
         lSum += Tree[dir].sum;
         return;
     }
     )
         findLeftMax(Tree[dir].l , l , r , L , L + R >> );
     )
         findLeftMax(Tree[dir].r , l , r , (L + R >> ) +  , R);
 }

 //二分check
 //为了方便处理这里的代码与上面的公式稍有不同
 inline bool check(int mid , int a , int b , int c , int d){
     lSum = rSum = ;
     lMax = rMax = -;
     findRightMax(root[mid] , a , b -  ,  , N);
     findLeftMax(root[mid] , c +  , d ,  , N);
      , N) + lMax + rMax >= ;
 }

 int main(){
     N = read();
     ;
      ; i <= N ; i++)
         num[sorted[i].ind = i] = sorted[i].num = read();
     init(root[] =  ,  , N);
     sort(sorted +  , sorted + N + );
      ; i <= N ; i++)
         update(root[i + ] = ++cntNode , root[i] ,  , N , sorted[i].ind);
     for(int Q = read() ; Q ; Q--){
          , b = (read() + lastans) % N +  , c = (read() + lastans) % N +  , d = (read() + lastans) % N + ;
         if(a > b)
             swap(a , b);
         if(a > c)
             swap(a , c);
         if(a > d)
             swap(a , d);
         if(b > c)
             swap(b , c);
         if(b > d)
             swap(b , d);
         if(c > d)
             swap(c , d);
          , r = N;
         while(l < r){
              >> ;
             if(check(mid , a , b , c , d))
                 l = mid;
             else
                 r = mid - ;
         }
         printf("%d\n" , lastans = sorted[l].num);
     }
     ;
 }

Luogu2839 Middle 主席树、二分答案的更多相关文章

  1. BZOJ 2653: middle(主席树+二分答案)

    传送门 解题思路 首先可以想到一种暴力做法,就是询问时二分,然后大于等于这个值的设为1,否则设为-1,然后就和GSS1那样统计答案.但是发现这样时间空间复杂度都很爆炸,所以考虑预处理,可以用主席树来做 ...

  2. 【bzoj2653】【middle】【主席树+二分答案】

    Description 一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整. 给你一个长度为 n 的序列 s .回答 ...

  3. bzoj 2653: middle (主席树+二分)

    2653: middle Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2522  Solved: 1434[Submit][Status][Disc ...

  4. [BZOJ2653]middle 主席树+二分

    2653: middle Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2042  Solved: 1123[Submit][Status][Disc ...

  5. BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)

    BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案) 题意 : 给你一个长为\(R\)宽为\(C\)的矩阵,第\(i\)行\(j\)列的数为\(P_{i,j}\). 有\(m\)次 ...

  6. BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案

    题目链接: CTSC2018混合果汁 显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案. 那么我们二分一个美味度作为答案然后考虑如何验证? 选择时显然要贪心的 ...

  7. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  8. BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案

    Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...

  9. HDU - 6621 K-th Closest Distance 主席树+二分答案

    K-th Closest Distance 主席树第二波~ 题意 给你\(n\)个数\(m\)个询问,问\(i\in [l,r]\)计算每一个\(|a_{i}-p|\)求出第\(k\)小 题目要求强制 ...

随机推荐

  1. Html/Css 初步认识笔记

    1.什么是 HTML ? HTML(HyperText Markup Language) 的学名是超文本标记语言. 标记用来表示网页内容要如何显示,自身不显示 .<我就是标记> 标记成对出 ...

  2. AppBoxPro(权限管理框架--FineUIPro基础版+工厂模式+ADO.NET+存储过程)

    FineUIPro基础版火爆来袭,特献上ADO.NET纯SQL方式AppBoxPro,希望大家能够喜欢! 下载源码请到[知识星球] https://t.zsxq.com/3rrNFyv

  3. Expo大作战(三十四)--expo sdk api之LinearGradient(线性渐变),KeepAwake(保持屏幕不休眠),IntentLauncherAndroid,Gyroscope,

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  4. (网页)angularjs中的验证input输入框只能输入数字和小数点

    百度的资料:自己记录看下 把js的验证方法改成angular可使用的方法 AngularJS文件的写法: $scope.clearNoNum = function(obj,attr){ //先把非数字 ...

  5. Python基础点

    写这篇的目的并不是要把python的基础知识汇总一遍,而是着重记录一些实际编写代码时遇到的常用/重要的内容 以点的形式记录,之后遇到的内容会慢慢补充进来 1. 斜杠 / :斜字第一笔, 转义用反斜杠 ...

  6. Postgresql_根据执行计划优化SQL

    执行计划路径选择 postgresql查询规划过程中,查询请求的不同执行方案是通过建立不同的路径来表达的,在生成许多符合条件的路径之后,要从中选择出代价最小的路径,把它转化为一个计划,传递给执行器执行 ...

  7. 第七章 鼠标(CHECKER4)

    /*--------------------------------------------- CHECKER4.C -- Mouse Hit-Test Demo Program No.4 (c) C ...

  8. 字符串通过在配置文件配置三个key来进行加密解密

    在这里和大家分享一个加密util,相对于md5加密相信大家都已经很熟悉了吧,md5是不可逆的一种加密方式,虽说不可逆但是网上已经有了破解的方法,我这边分享一个免费的破解 网址给大家:https://w ...

  9. 【PAT】B1042 字符统计(20 分)

    /* 15分的题很简单,但是自己写的时候在输入数据时没有考虑好下标 另外有忘记了输入字符时考虑是否有\n */ #include<stdio.h> #include<algorith ...

  10. nginx代理 upstream轮询

    问题描述 我有2个Tomcat  一个端口开启(8021),一个端口未开启(8022),在nginx里用upstream模块进行代理 ,代理的负载算法采用的是默认的轮询算法,配置成功后,访问页面时并没 ...