Luogu2839 Middle 主席树、二分答案
题目传送门: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 主席树、二分答案的更多相关文章
- BZOJ 2653: middle(主席树+二分答案)
传送门 解题思路 首先可以想到一种暴力做法,就是询问时二分,然后大于等于这个值的设为1,否则设为-1,然后就和GSS1那样统计答案.但是发现这样时间空间复杂度都很爆炸,所以考虑预处理,可以用主席树来做 ...
- 【bzoj2653】【middle】【主席树+二分答案】
Description 一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整. 给你一个长度为 n 的序列 s .回答 ...
- bzoj 2653: middle (主席树+二分)
2653: middle Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2522 Solved: 1434[Submit][Status][Disc ...
- [BZOJ2653]middle 主席树+二分
2653: middle Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2042 Solved: 1123[Submit][Status][Disc ...
- BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)
BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案) 题意 : 给你一个长为\(R\)宽为\(C\)的矩阵,第\(i\)行\(j\)列的数为\(P_{i,j}\). 有\(m\)次 ...
- BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案
题目链接: CTSC2018混合果汁 显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案. 那么我们二分一个美味度作为答案然后考虑如何验证? 选择时显然要贪心的 ...
- P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案
$ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串 ——后缀数组 ST表 主席树 二分答案
Solution 1: 后缀数组暴力大法好 #include <map> #include <cmath> #include <queue> #include &l ...
- HDU - 6621 K-th Closest Distance 主席树+二分答案
K-th Closest Distance 主席树第二波~ 题意 给你\(n\)个数\(m\)个询问,问\(i\in [l,r]\)计算每一个\(|a_{i}-p|\)求出第\(k\)小 题目要求强制 ...
随机推荐
- Bootstrap里的文件分别代表什么意思及其引用方法
关于Bootstrap打包的文件分别代表什么意思,官网也没有给出一个明确的解释,在网上查了一些资料,总价归纳了如下: bootstrap/ <!--主目录--> ├── css/ < ...
- 运行gulp项目报错:AssertionError: Task function must be specified。
一.问题描述: gulp项目在本地windows 10机器上跑没有任何问题,但是放在centos 7虚拟机上跑报错:AssertionError: Task function must be spec ...
- Git应用—01初始化项目
1.环境变量GIT_HOME D:\GreenSoftware\PortableGit Path %GIT_HOME%\cmd; 2.初始化git config --global u ...
- 怎么查找Jenkins的个人api token
程序中可变部分解释:其中server.build_job方法传入的参数channel为分渠道构建参数,也即jenkins job的参数,这个参数随不同的日常job不同是不同的,实际编写脚本的过程中这个 ...
- 使用缓存方式优化递归函数与lru_cache
一.递归函数的弊端 递归函数虽然编写时用很少的代码完成了庞大的功能,但是它的弊端确实非常明显的,那就是时间与空间的消耗. 用一个斐波那契数列来举例 import time #@lru_cache(20 ...
- php程序开发之实现网页跳转
php程序开发之实现网页跳转的三种方式 2017年04月16日 20:44:14 阅读数:3352 PHP目前是用来开发WEB项目的首选语言.Web项目中,从一个网页跳转到另一个网页是最常用的技术之一 ...
- Iptables防火墙(SNAT和DNAT)
1.SNAT:源地址转换 实现内网访问外网,修改IP地址,使用POSTROUTING 命令:iptables -t nat -A POSTROUTING -s 192.168.1.10/2 ...
- 在同一个服务器(同一个IP)为不同域名绑定的免费SSL证书
越来越多的浏览器不在支持http协议了,这就要求你为你的网站必须绑定SSL证书.谷歌浏览器也将要在今年取消对http协议的支持,申请CA证书迫在眉睫.我购买有两个域名,一个虚拟机,没事鼓捣鼓捣,图个乐 ...
- do-while语句及for语句(初学者)
1.do-while语句的一般形式为: do 语句 while(表达式): 这个循环与while循环的不同在于:它先执行循环中的语句,然后再判断这个表达式是否为真,如果为真则继续循环:如果为假,则中止 ...
- Python3编写网络爬虫08-数据存储方式一-文件存储
数据存储 用解析器解析出数据之后,就是存储数据了.保存的形式可以多种多样,最简单的形式是直接保存为文本文件,如TXT JSON CSV等.另外还可以保存到数据库中,如关系型数据库MySQL 非关系型数 ...