BZOJ 3065 带插入区间第K小值
题目描述
Description
从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。
Input
第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
第三行一个正整数q,表示下面有多少个操作。
下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
- Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。
(1 <= x <= y <= m, 1 <= k <= y - x + 1) - M x val: 将从左至右第x只跳蚤的弹跳力改为val。
(1 <= x <= m) - I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。
(1 <= x <= m + 1)
为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
则输入的时候实际是:
Q _x _y _k ------> 表示 Q _x^lastAns _y^lastAns _k^lastAns
M _x _val ------> 表示 M _x^lastAns _val^lastAns
I _x _val ------> 表示 I _x^lastAns _val^lastAns
简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。
(祝Pascal的同学早日转C++,就不提供pascal版的描述了。)
Sample Input
10
10 5 8 28 0 19 2 31 1 22
30
I 6 9
M 1 11
I 8 17
M 1 31
M 6 26
Q 2 7 6
I 23 30
M 31 7
I 22 27
M 26 18
Q 26 17 31
I 5 2
I 18 13
Q 3 3 3
I 27 19
Q 23 23 30
Q 5 13 5
I 3 0
M 15 27
Q 0 28 13
Q 3 29 11
M 2 8
Q 12 5 7
I 30 19
M 11 19
Q 17 8 29
M 29 4
Q 3 0 12
I 7 18
M 29 27
Sample Output
28
2
31
0
14
15
14
27
15
14
题目大意
略.
题解
替罪羊树模板题.
用一棵替罪羊树维护序列, 每个节点上开一棵线段树, 维护以这个点为根的子树下的所有权值.
考虑以下操作:
- 插入: 直接在替罪羊树上插入即可, 正常重建.
- 修改: 直接修改.
- 查询: 这个比较麻烦. 我的做法是在替罪羊树上找到所有被这个区间覆盖的点, 分为以下两类
- 这个点以及以它为根的子树都被题目所求区间包含, 则将这个点上的线段树扔进vector中;
- 这个点被区间包含, 但并非以它为根的子树都被包含, 则在一棵另开的线段树上插入这个点对应的序列上的权值, 并递归其子树.
在得到的最多\(\log n\)棵线段树上二分即可
这样的做法是可以保证时空复杂度均为\(n \log^2 n\), 还是可以接受的.
注意空间释放的问题, 我只会写动态的指针式写法, 有些大佬说可以用数组静态模拟, 但我没想懂怎么释放内存, 总之就是不会.
代码跑得很慢, 可能是频繁申请 / 释放空间导致的吧
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
sgn *= -1;
while(isdigit(c))
a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
inline char getChar()
{
char c;
while(! isgraph(c = getchar()));
return c;
}
inline void print(int a)
{
if(! a)
return;
print(a / 10);
putchar(a % 10 + '0');
}
inline void println(int a)
{
if(a < 0)
putchar('-'), a *= -1;
if(a == 0)
putchar('0');
print(a);
putchar('\n');
}
}
const int N = 35000, Q = 35000, BND = 70000;
const double ALPH = 0.75;
std::vector<int> cpy;
struct segmentTree
{
struct node
{
node* suc[2];
int sum;
inline node()
{
suc[0] = suc[1] = NULL;
sum = 0;
}
};
node* rt;
inline segmentTree()
{
rt = NULL;
}
node* modify(node* u, int opt, int L, int R, int w)
{
if(u == NULL)
u = new node;
u->sum += opt;
if(L ^ R)
{
int mid = L + R >> 1;
if(w <= mid)
u->suc[0] = modify(u->suc[0], opt, L, mid, w);
else
u->suc[1] = modify(u->suc[1], opt, mid + 1, R, w);
}
return u;
}
inline void modify(int pos, int opt)
{
rt = modify(rt, opt, 0, BND, pos);
}
inline void insert(int w)
{
rt = modify(rt, 1, 0, BND, w);
}
void clear(node *u)
{
for(int i = 0; i < 2; ++ i)
if(u->suc[i] != NULL)
clear(u->suc[i]);
delete u;
}
inline void clear()
{
if(rt != NULL)
clear(rt), rt = NULL;
}
inline void Delete()
{
clear();
delete this;
}
};
segmentTree sgt;
std::vector<segmentTree::node*> nd, _nd;
struct scapegoatTree
{
struct node
{
node *suc[2];
int sz, w;
segmentTree *rec;
inline node(int _w)
{
sz = 0, w = _w;
suc[0] = suc[1] = NULL;
rec = new segmentTree;
}
inline int check()
{
int sucSz[2] = {0, 0};
for(int i = 0; i < 2; ++ i)
if(suc[i] != NULL)
sucSz[i] = suc[i]->sz;
return std::max(sucSz[0], sucSz[1]) > sz * ALPH;
}
};
node *rt;
node* build(int L, int R)
{
int mid = L + R >> 1;
node *u = new node(cpy[mid]);
if(L < mid)
u->suc[0] = build(L, mid - 1);
if(R > mid)
u->suc[1] = build(mid + 1, R);
for(int i = L; i <= R; ++ i)
u->rec->insert(cpy[i]), ++ u->sz;
return u;
}
int flg;
void DFS(node* u)
{
u->rec->Delete();
if(u->suc[0] != NULL)
DFS(u->suc[0]);
cpy.push_back(u->w);
if(u->suc[1] != NULL)
DFS(u->suc[1]);
delete u;
}
inline node* rebuild(node *u)
{
cpy.clear();
DFS(u);
u = build(0, cpy.size() - 1);
flg = 1;
return u;
}
node* insert(node* u, int pos, int w)
{
int tmp = u == NULL || u->suc[0] == NULL ? 0 : u->suc[0]->sz;
if(u == NULL)
u = new node(w);
else if(pos <= tmp + 1)
u->suc[0] = insert(u->suc[0], pos, w);
else
u->suc[1] = insert(u->suc[1], pos - tmp - 1, w);
++ u->sz;
u->rec->insert(w);
if(u->check())
u = rebuild(u);
return u;
}
inline void insert(int pos, int w)
{
flg = 0;
rt = insert(rt, pos, w);
}
void get(node* u, int L, int R)
{
if(L == 1 && R == u->sz)
{
nd.push_back(u->rec->rt);
return;
}
int tmp = u->suc[0] == NULL ? 0 : u->suc[0]->sz;
if(tmp + 1 >= L && tmp + 1 <= R)
sgt.insert(u->w);
if(u->suc[0] != NULL && L <= tmp)
get(u->suc[0], L, std::min(tmp, R));
if(u->suc[1] != NULL && R >= tmp + 2)
get(u->suc[1], std::max(L, tmp + 2) - tmp - 1, R - tmp - 1);
}
inline int query(int x, int y, int k)
{
nd.clear();
sgt.clear();
get(rt, x, y);
if(sgt.rt != NULL)
nd.push_back(sgt.rt);
int L = 0, R = BND;
while(L ^ R)
{
int cnt = nd.size(), sum = 0;
for(int i = 0; i < cnt; ++ i)
sum += nd[i]->suc[0] == NULL ? 0 : nd[i]->suc[0]->sum;
_nd.clear();
for(int i = 0; i < cnt; ++ i)
if(nd[i]->suc[sum < k] != NULL)
_nd.push_back(nd[i]->suc[sum < k]);
nd = _nd;
if(sum >= k)
R = (L + R) >> 1;
else
L = ((L + R) >> 1) + 1, k -= sum;
}
return L;
}
int modify(node* u, int pos, int w)
{
int tmp = u->suc[0] == NULL ? 0 : u->suc[0]->sz;
if(pos == tmp + 1)
{
u->rec->modify(u->w, -1), u->rec->modify(w, 1);
int res = u->w;
u->w = w;
return res;
}
int res;
if(u->suc[0] != NULL && pos <= u->suc[0]->sz)
res = modify(u->suc[0], pos, w);
else
res = modify(u->suc[1], pos - tmp - 1, w);
u->rec->modify(res, -1), u->rec->modify(w, 1);
return res;
}
inline void modify(int pos, int w)
{
modify(rt, pos, w);
}
}seq;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ3065.in", "r", stdin);
freopen("BZOJ3065.out", "w", stdout);
#endif
using namespace Zeonfai;
int n = getInt();
for(int i = 0; i < n; ++ i)
cpy.push_back(getInt());
seq.rt = seq.build(0, n - 1);
int Q = getInt(), lst = 0;
for(int i = 0; i < Q; ++ i)
{
char opt = getChar();
if(opt == 'I')
{
int pos = getInt() ^ lst, w = getInt() ^ lst;
seq.insert(pos, w);
}
else if(opt == 'Q')
{
int x = getInt() ^ lst, y = getInt() ^ lst, k = getInt() ^ lst;
println(lst = seq.query(x, y, k));
}
else if(opt == 'M')
{
int x = getInt() ^ lst, w = getInt() ^ lst;
seq.modify(x, w);
}
}
}
BZOJ 3065 带插入区间第K小值的更多相关文章
- 「BZOJ3065」带插入区间第K小值 替罪羊树×线段树
题目描述 从前有\(n\)只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力\(a_i\).跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间\(k\)小值.他 ...
- [bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]
题面 传送门 思路 发现强制在线了...... 本来可以树套树解决的问题,现在外层不能使用线段树了,拿什么替代呢? 我们需要一种支持单点插入.下套数据结构.数据结构上传合并复杂度最多单log,不能旋转 ...
- bzoj 3065: 带插入区间K小值 替罪羊树 && AC300
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1062 Solved: 253[Submit][Status] Des ...
- 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树
题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...
- BZOJ 3065 带插入区间K小值(sag套线段树)
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 4696 Solved: 1527[Submit][Status][Di ...
- bzoj 3065: 带插入区间K小值(分块)
Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它 ...
- BZOJ 3065 带插入区间K小值
http://www.lydsy.com/JudgeOnline/problem.php?id=3065 思路:替罪羊树套权值线段树. 当替罪羊树某个子树大于某个比利(比例)时就暴力重构,本题时间复杂 ...
- BZOJ 3065 带插入区间K小值 (替罪羊树套线段树)
毒瘤题.参考抄自博客:hzwer 第一次写替罪羊树,完全是照着题解写的,发现这玩意儿好强啊,不用旋转每次都重构还能nlognnlognnlogn. 还有外面二分和里面线段树的值域一样,那么r = mi ...
- 少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小
少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写 ...
随机推荐
- python3爬取墨迹天气并发送给微信好友,附源码
需求: 1. 爬取墨迹天气的信息,包括温湿度.风速.紫外线.限号情况,生活tips等信息 2. 输入需要查询的城市,自动爬取相应信息 3. 链接微信,发送给指定好友 思路比较清晰,主要分两块,一是爬虫 ...
- IOC容器和Bean的配置实例
实验1: <!--实验1:通过IOC容器创建对象,并为属性赋值 --> <!-- 需要由IOC容器创建对象的全类名 --> <!-- 为了便于从IOC容器中获取book对 ...
- poj 2236 网络连接问题 并查集
题意:n台电脑,当两台之间的距离小于d的时候可以连接. 题目会进行操作“修复”还有“查询是否已经连接”.只要在查询的时候输出YES或者ON 思路: 把可以相互连接的 即两者之间的距离小于 d q[i ...
- dict 字典的常用操作
#dict 字典的常用操作: id_db.get() #获取 id_db.update() #更新(覆盖)字典 id_db.values() #打印字典里所有的values id_db.keys() ...
- Linux网络编程:客户端/服务器的简单实现
一. Socket的基本知识 1. socket功能 Socket层次 Socket实质上提供了进程通信的端点,进程通信之前,双方必须首先各自创建一个端点,否则是没有办法建立联系并相互通信的. 每一个 ...
- HDU 2242 双连通分量 考研路茫茫——空调教室
思路就是求边双连通分量,然后缩点,再用树形DP搞一下. 代码和求强连通很类似,有点神奇,=_=,慢慢消化吧 #include <cstdio> #include <cstring&g ...
- Linux内存cache/buffer剖析
查询linux系统中空闲内存/内存使用状态查看/剩余内存查看 如何计算内存的使用量及空闲量 物理已用内存 = 实际已用内存 - 缓冲 - 缓存 = 24752 - 283 ...
- 树状数组 - BZOJ 1103 [POI2007]大都市
bzoj 1103 [POI2007]大都市 描述 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员 Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景. ...
- 转 关于oracle 分区表 表空间以及索引的总结
关于oracle的表空间,分区表,以及索引的总结关键字: oracle, 表空间, 分区表, 索引 上周第一次做数据库测试碰到了很多问题特此总结: 表空间: Oracle的UNDOTBS01.DBF文 ...
- Apache JMeter汉化手册
/*杜绝抄袭,分享请注明链接:http://www.cnblogs.com/yana789 Apache JMeter 应用是纯java开源的应用工具,压力测试和负载测试的容易上手工具. Apache ...