【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树
题目描述
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c。如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
输入
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
输出
输出每个询问的结果
样例输入
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
样例输出
1
2
1
题解
本蒟蒻并不会写整体二分,所以写了树套树
17.12.23 UPD:比树套树优雅到不知哪里去了的整体二分题解
其实这道题想到方法的话实现起来还是非常容易的。
注意题目中说的是“每个位置加入一个数c”,不是“加上”,也就是说不必支持修改,但必须支持插入。
所以需要选择权值线段树或Treap。
如果把权值线段树或Treap放在内层,区间线段树放在外层,那么会不方便查询(详见 bzoj3194 中子任务2),时间复杂度为O(nlog^3n),TLE;同时也不便于修改。
所以不能把权值线段树或Treap放在内层,必须放在外层,外层就只能选择权值线段树。
把权值线段树放在外层,区间线段树放在内层的话,每个最内层节点表示区间内权值在指定范围内的数的个数。
这样修改时在外层查找对应区间,在内层区间+1,使用lazy标记可以保证时间复杂度。
查询时查的是第k大,所以需要先确定范围。如果权值线段树的右子树对应的区间线段树的区间和(线段树区间查询)大于等于k,即右半部分权值中含有k大数,则在右边查找;否则在左边查找。
注意要把两棵树分开(表示代码可能分的不太清楚。。。),千万不要弄混。
在我的代码中,外层权值线段树是用一般的完全二叉树储存方式(x<<1,x<<1|1),而内层区间线段树是用动态开点的储存方式。
而这里的pushdown、update和query这前三个函数是内层区间线段树的函数;modify、solve是外层权值线段树的函数。
另外,数据经加强后会有负数,所以应把原数+n+1处理。
另外,本题会爆int,而long long可能会TLE,最好是用unsigned int。
#include <cstdio>
#include <algorithm>
#define N 1000010
#define M 20000010
using namespace std;
int n , root[N] , ls[M] , rs[M] , tot;
unsigned sum[M] , tag[M];
void pushdown(int l , int r , int x)
{
if(tag[x])
{
int mid = (l + r) >> 1;
if(!ls[x]) ls[x] = ++tot;
if(!rs[x]) rs[x] = ++tot;
sum[ls[x]] += (mid - l + 1) * tag[x] , tag[ls[x]] += tag[x];
sum[rs[x]] += (r - mid) * tag[x] , tag[rs[x]] += tag[x];
tag[x] = 0;
}
}
void update(int b , int e , int l , int r , int &x)
{
if(!x) x = ++tot;
if(b <= l && r <= e)
{
sum[x] += (r - l + 1) , tag[x] ++ ;
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) update(b , e , l , mid , ls[x]);
if(e > mid) update(b , e , mid + 1 , r , rs[x]);
sum[x] = sum[ls[x]] + sum[rs[x]];
}
unsigned query(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return sum[x];
pushdown(l , r , x);
int mid = (l + r) >> 1;
unsigned ans = 0;
if(b <= mid) ans += query(b , e , l , mid , ls[x]);
if(e > mid) ans += query(b , e , mid + 1 , r , rs[x]);
return ans;
}
void modify(int b , int e , int p , int l , int r , int x)
{
update(b , e , 1 , n , root[x]);
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) modify(b , e , p , l , mid , x << 1);
else modify(b , e , p , mid + 1 , r , x << 1 | 1);
}
int solve(int b , int e , unsigned a , int l , int r , int x)
{
if(l == r) return l;
int mid = (l + r) >> 1;
unsigned tmp = query(b , e , 1 , n , root[x << 1 | 1]);
if(tmp >= a) return solve(b , e , a , mid + 1 , r , x << 1 | 1);
else return solve(b , e , a - tmp , l , mid , x << 1);
}
int main()
{
int m , opt , x , y , z;
unsigned t;
scanf("%d%d" , &n , &m);
while(m -- )
{
scanf("%d%d%d" , &opt , &x , &y);
if(opt == 1) scanf("%d" , &z) , modify(x , y , z + n + 1 , 1 , 2 * n + 1 , 1);
else scanf("%u" , &t) , printf("%d\n" , solve(x , y , t , 1 , 2 * n + 1 , 1) - n - 1);
}
return 0;
}
【bzoj3110】[Zjoi2013]K大数查询 权值线段树套区间线段树的更多相关文章
- BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树
题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是 ...
- 洛谷P3332 [ZJOI2013]K大数查询 权值线段树套区间线段树_标记永久化
Code: #include <cstdio> #include <algorithm> #include <string> #include <cstrin ...
- [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)
[BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...
- BZOJ3110[Zjoi2013]K大数查询(树状数组+整体二分)
3110 [Zjoi2013]K大数查询 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a ...
- BZOJ3110 [Zjoi2013]K大数查询 树套树 线段树 整体二分 树状数组
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3110 题意概括 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位 ...
- [BZOJ3110] [Zjoi2013] K大数查询 (树套树)
Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置 ...
- bzoj3110 [Zjoi2013]K大数查询——线段树套线段树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110 外层权值线段树套内层区间线段树: 之所以外层权值内层区间,是因为区间线段树需要标记下传 ...
- BZOJ3110: [Zjoi2013]K大数查询
喜闻乐见的简单树套树= =第一维按权值建树状数组,第二维按下标建动态开点线段树,修改相当于第二维区间加,查询在树状数组上二分,比一般的线段树还短= =可惜并不能跑过整体二分= =另外bzoj上的数据有 ...
- bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】
//========================== 蒟蒻Macaulish:http://www.cnblogs.com/Macaulish/ 转载要声明! //=============== ...
随机推荐
- 模仿淘宝首页写的高仿页面,脚本全用的原生JS,菜鸟一枚高手看了勿喷哈
自己仿照淘宝首页写的页面,仿真度自己感觉可以.JS脚本全是用原生JavaScript写得,没用框架.高手看了勿喷,请多多指正哈!先上网页截图看看效果,然后上源码: 上源码,先JavaScript : ...
- Java设计模式(21)——行为模式之备忘录模式(Memento)
一.概述 概念 UML简图 角色 根据下图得到角色 备忘录角色(Memento).发起人角色(Originator).负责人角色(Caretaker) 二.实践 使用白箱实现,给出角色的代码: 发起人 ...
- Java线程和多线程(十二)——线程池基础
Java 线程池管理多个工作线程,其中包含了一个队列,包含着所有等待被执行的任务.开发者可以通过使用ThreadPoolExecutor来在Java中创建线程池. 线程池是Java中多线程的一个重要概 ...
- path.resolve()和path.join()的区别
path.join() 组装路径.该方法的主要用途在于,会正确使用当前系统的路径分隔符,Unix系统是/,Windows系统是\.路径字符中可以使用..或../进行相对路径的计算,其它路径表示符会被 ...
- Ubuntu主题美化篇
主题美化篇 Ubuntu自带的主题简直不敢恭维,这里博主将它美化了一番,心情瞬间都好了一大截,码代码也会飞起!!先放一张我美化后的效果. 桌面和终端效果如下: unity-tweak-tool 调整 ...
- Android 修改系统默认density
如你所知在Anroid N 中,系统添加了多个级别的密度值供用户选择. 系统的默认的值就是 ro.sf.lcd_density 同时其他级别的默认值的大小基础也是以默认值为基础,然后乘以不同的比例得到 ...
- 使用pycharm软件配置数据库可视化
必须品: pycharm软件,专业版最好自带就有,社区版就需要安装下插件. 专业版直接会在右边的编辑框浮动,直接点开就可以配置. 如图所示,点开就可以配置相应的数据库, 点开配置完毕就可以使用了. 还 ...
- Oracle存储过程练习题
1.1.创建一个过程,能向dept表中添加一个新记录.(in参数) 创建过程 create or replace procedure insert_dept ( num_dept in number, ...
- 【button】 按钮组件说明
原型: <button size="[default | mini]" type="[primary | default | warn]" plain=& ...
- 利用nohup后台运行jar文件包程序
Linux 运行jar包命令如下: 方式一: java -jar XXX.jar特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 那如何让窗口不锁定? 方式二 j ...