题目很好,考察对主席树的深入理解与灵活运用。

首先看看一般解决中位数的思路,我们二分一个 \(mid\),将区间中 \(\ge mid\) 的数置为 \(1\),小于的置为 \(-1\),然后求区间和,若大于等于零则 \(mid\) 还能增大,否则减小。

现在就有了两个问题:第一,区间不固定;第二,每次二分一个答案就要重构区间,复杂度爆炸。

现在我们来仔细分析一下主席树的结构,首先,它是一个每个点都建了一棵线段树,形成前缀和的形式;每棵线段树又与区间有关。抽象地说,我们可以把第一个特征看作解决时间这一维限制,第二个特征解决位置这一维限制,即主席树同时解决了两维限制。

那再来找找这题的两维限制。如果我们把每次二分看做时间先后的操作,将每次二分的值作为一个“点”建线段树,就相当于预处理出了每次二分后区间的情况,省去了重构。再把权值离散化,那么就映射到了 \(1\sim n\) 的区间,对于 \(mid+1\),显然只有 \(mid\) 这个权值由 \(1\) 变成了 \(-1\) ,这其实只是一个单点修改的操作,这样就解决了第二个问题。

对于第一个问题,我们可以用最大子段和的思路维护 \(lmax,rmax,sum\) 的 tag,那么对于题目给出的区间 \([a,b],[c,d]\) ,答案即是 \([a,b]\) 的 \(rmax\)、\([b+1,c-1]\) 的 \(sum\),\([c,d]\) 的 \(lmax\) 之和。

哪里没有讲清楚可以看代码进一步理解。

  1. #include <bits/stdc++.h>
  2. #define l(x) t[x].l
  3. #define r(x) t[x].r
  4. using namespace std;
  5. const int N=1e5+5;
  6. struct Tree
  7. {
  8. int l,r,lmax,rmax,sum;
  9. void clear() {lmax=rmax=-N,l=r=sum=0;}
  10. }t[N*20],Ans;
  11. int n,Q,a[N],q[4],root[N],cntnode,id[N],ans;
  12. void build(int &rt,int l,int r)
  13. {
  14. t[rt=++cntnode]=(Tree){0,0,r-l+1,r-l+1,r-l+1};
  15. if(l==r) return; int mid=l+r>>1;
  16. build(l(rt),l,mid); build(r(rt),mid+1,r);
  17. }
  18. inline void pushup(int rt)
  19. {
  20. t[rt].lmax=max(t[l(rt)].lmax,t[l(rt)].sum+t[r(rt)].lmax);
  21. t[rt].rmax=max(t[r(rt)].rmax,t[r(rt)].sum+t[l(rt)].rmax);
  22. t[rt].sum=t[l(rt)].sum+t[r(rt)].sum;
  23. }
  24. void Insert(int &rt,int pre,int l,int r,int pos)
  25. {
  26. t[rt=++cntnode]=t[pre];
  27. if(l==r) {t[rt].lmax=t[rt].rmax=t[rt].sum=-1; return;}
  28. int mid=l+r>>1;
  29. if(pos<=mid) Insert(l(rt),l(pre),l,mid,pos);
  30. else Insert(r(rt),r(pre),mid+1,r,pos);
  31. pushup(rt);
  32. }
  33. void query(int rt,int lc,int rc,int l,int r)
  34. {
  35. if(l<=lc&&r>=rc)
  36. {
  37. Ans.lmax=max(Ans.lmax,Ans.sum+t[rt].lmax);
  38. Ans.rmax=max(t[rt].rmax,Ans.rmax+t[rt].sum);
  39. Ans.sum+=t[rt].sum;
  40. return;
  41. }
  42. int mid=lc+rc>>1;
  43. if(l<=mid) query(l(rt),lc,mid,l,r);
  44. if(r>mid) query(r(rt),mid+1,rc,l,r);
  45. }
  46. bool check(int mid)
  47. {
  48. int res=0;
  49. if(q[1]+1<=q[2]-1)
  50. Ans.clear(),query(root[mid],1,n,q[1]+1,q[2]-1),res+=Ans.sum;
  51. Ans.clear(),query(root[mid],1,n,q[0],q[1]),res+=Ans.rmax;
  52. Ans.clear(),query(root[mid],1,n,q[2],q[3]),res+=Ans.lmax;
  53. return res>=0;
  54. }
  55. int main()
  56. {
  57. scanf("%d",&n);
  58. for(int i=1;i<=n;++i) scanf("%d",a+i),id[i]=i;
  59. build(root[1],1,n);
  60. sort(id+1,id+n+1,[](int x,int y){return a[x]<a[y];});
  61. for(int i=2;i<=n;++i) Insert(root[i],root[i-1],1,n,id[i-1]);
  62. scanf("%d",&Q);
  63. while(Q--)
  64. {
  65. for(int i=0;i<4;++i)
  66. scanf("%d",q+i),q[i]=(q[i]+ans)%n+1;
  67. sort(q,q+4); int l=1,r=n;
  68. while(l<=r)
  69. {
  70. int mid=l+r>>1;
  71. if(check(mid)) ans=a[id[mid]],l=mid+1;
  72. else r=mid-1;
  73. }
  74. printf("%d\n",ans);
  75. }
  76. return 0;
  77. }

Luogu2839 [国家集训队]middle 题解的更多相关文章

  1. luogu2839 [国家集训队]middle

    题目链接:洛谷 题目大意:给定一个长度为$n$的序列,每次询问左端点在$[a,b]$,右端点在$[c,d]$的所有子区间的中位数的最大值.(强制在线) 这里的中位数定义为,对于一个长度为$n$的序列排 ...

  2. 【LG2839】[国家集训队]middle

    [LG2839][国家集训队]middle 题面 洛谷 题解 按照求中位数的套路,我们二分答案\(mid\),将大于等于\(mid\)的数设为\(1\),否则为\(-1\). 若一个区间和大于等于\( ...

  3. [国家集训队]middle 解题报告

    [国家集训队]middle 主席树的想法感觉挺妙的,但是这题数据范围这么小,直接分块草过去不就好了吗 二分是要二分的,把\(<x\)置\(-1\),\(\ge x\)的置\(1\),于是我们需要 ...

  4. [国家集训队]middle

    [国家集训队]middle 题目 解法 开\(n\)颗线段树,将第\(i\)颗线段树中大于等于第\(i\)小的数权值赋为1,其他的则为-1,对于每个区间维护一个区间和,最大前缀和,最大后缀和. 然后二 ...

  5. P2839 [国家集训队]middle

    P2839 [国家集训队]middle 好妙的题啊,,,, 首先二分一个答案k,把数列里>=k的数置为1,=0就是k>=中位数,<0就是k<中位数 数列的最大和很好求哇 左边的 ...

  6. CF484E Sign on Fence && [国家集训队]middle

    CF484E Sign on Fence #include<bits/stdc++.h> #define RG register #define IL inline #define _ 1 ...

  7. [洛谷P2839][国家集训队]middle

    题目大意:给你一个长度为$n$的序列$s$.$Q$个询问,问在$s$中的左端点在$[a,b]$之间,右端点在$[c,d]$之间的子段中,最大的中位数. 强制在线. 题解:区间中位数?二分答案,如果询问 ...

  8. BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)

    BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...

  9. 解题:国家集训队 Middle

    题面 求中位数的套路:二分,大于等于的设为1,小于的设为-1 于是可以从小到大排序后依次加入可持久化线段树,这样每次只会变化一个位置 那左右端点是区间怎么办? 先把中间的算上,然后维护每个区间左右两侧 ...

随机推荐

  1. application/octet-stream二进制流, springboot项目, postman 测试参数设置

    这里使用 原生String接受,之后解析,

  2. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  3. 三、Nginx设置用户认证

    要求:通过nginx服务端配置实现以下目标 访问web页面需要进行用户认证. 用户名为:tom,密码:123456 操作步骤, 更改配置文件 [root@client ~]# vim /usr/loc ...

  4. 合宙Luat | Cat.1 Socket数据收不到?学会两招不掉线

    1 服务器收不到Socket数据的原因 Socket是大家使用Cat.1模块常用的功能之一,但Cat.1模块不是直接跟服务器连接,而是通过NAT(即网络地址转换)与服务器连接. 一个会话建立后会在NA ...

  5. UNREFERENCED_PARAMETER的用处

    UNREFERENCED_PARAMETER的用处 作用:告诉编译器,已经使用了该变量,不必检测警告! 在VC编译器下,如果您用最高级别进行编译,编译器就会很苛刻地指出您的非常细小的警告.当你生命了一 ...

  6. Linux命令大全之查看登陆用户信息

    1,w +回车 2.who +回车 3.last+回车 4.lastlog+回车

  7. Tomcat与spring的类加载器案例

    Tomcat与spring的类加载器案例接下来将介绍<深入理解java虚拟机>一书中的案例,并解答它所提出的问题.(部分类容来自于书中原文) Tomcat中的类加载器在Tomcat目录结构 ...

  8. IDEA拷贝类路径

    1.方法一 1.1.鼠标右击需要复制的类 1.2.点击 Copy Reference 2.方法二 快捷键:Ctrl + Alt + Shift + C

  9. 怎么停掉或关闭运行的npm run dev

    可以直接Ctrl+C就会出现 输入是就可以了

  10. Linux 安装及管理程序

    Linux 安装及管理程序 目录 一.Linux应用程序基础 1.1.应用程序与系统命令的关系 1.2.典型应用程序的目录结构 1.2.常见的软件包封装类型 二.RPM包管理工具 2.1.RPM软件包 ...