题目描述

Description

从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。

这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。

这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。

这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)

这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。

请你帮一帮伏特吧。

快捷版题意:带插入、修改的区间k小值在线查询。

Input

第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。

第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。

第三行一个正整数q,表示下面有多少个操作。

下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)

  1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。

    (1 <= x <= y <= m, 1 <= k <= y - x + 1)
  2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。

    (1 <= x <= m)
  3. 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小值的更多相关文章

  1. 「BZOJ3065」带插入区间第K小值 替罪羊树×线段树

    题目描述 从前有\(n\)只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力\(a_i\).跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间\(k\)小值.他 ...

  2. [bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]

    题面 传送门 思路 发现强制在线了...... 本来可以树套树解决的问题,现在外层不能使用线段树了,拿什么替代呢? 我们需要一种支持单点插入.下套数据结构.数据结构上传合并复杂度最多单log,不能旋转 ...

  3. bzoj 3065: 带插入区间K小值 替罪羊树 && AC300

    3065: 带插入区间K小值 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1062  Solved: 253[Submit][Status] Des ...

  4. 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树

    题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...

  5. BZOJ 3065 带插入区间K小值(sag套线段树)

    3065: 带插入区间K小值 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 4696  Solved: 1527[Submit][Status][Di ...

  6. bzoj 3065: 带插入区间K小值(分块)

    Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它 ...

  7. BZOJ 3065 带插入区间K小值

    http://www.lydsy.com/JudgeOnline/problem.php?id=3065 思路:替罪羊树套权值线段树. 当替罪羊树某个子树大于某个比利(比例)时就暴力重构,本题时间复杂 ...

  8. BZOJ 3065 带插入区间K小值 (替罪羊树套线段树)

    毒瘤题.参考抄自博客:hzwer 第一次写替罪羊树,完全是照着题解写的,发现这玩意儿好强啊,不用旋转每次都重构还能nlognnlognnlogn. 还有外面二分和里面线段树的值域一样,那么r = mi ...

  9. 少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小

    少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写 ...

随机推荐

  1. micrium ucprobe使用笔记

    前段时间在学习ucos-iii的时候,用到了micrium ucprobe,发现在调试的时候,很方便,可以直观的看到任务的运行使用情况,全局变量的值变化等,当然详细的可以参考官方文档,也可以参考网上的 ...

  2. ACM-ICPC 2018 徐州赛区网络预赛 F. Features Track

    262144K   Morgana is learning computer vision, and he likes cats, too. One day he wants to find the ...

  3. bs4--官文--搜索文档树

    搜索文档树 Beautiful Soup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似,请读者举一反三. 再以“爱丽丝”文档作为例子: ht ...

  4. JQuery根据字母检索元素并导航到指定位置

    介绍:类似于实现ios通讯录中右侧字母,快速导航到联系人的效果,如图: Html代码如下,分别是字母和港星名字的排序: <input type="text" id=" ...

  5. Android开发——Android 6.0权限管理机制详解

    .Android 6.0运行时主动请求权限 3.1  检测和申请权限 下面的例子介绍上面列出的读写SD卡的使用例子,可以使用以下的方式解决: public boolean isGrantExterna ...

  6. Leetcode 477.汉明距离总和

    汉明距离总和 两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量. 计算一个数组中,任意两个数之间汉明距离的总和. 示例: 输入: 4, 14, 2 输出: 6 解释: 在二进制表示中, ...

  7. oracle中的dual表

    dual表是和Oracle数据字典一起创建的.它实际上只包含dummy这一个column,并且只有一条记录,这条记录的值是X. X dual表的owner是SYS,但所有用户都可以访问它.Althou ...

  8. 零基础自学用Python 3开发网络爬虫

    原文出处: Jecvay Notes (@Jecvay) 由于本学期好多神都选了Cisco网络课, 而我这等弱渣没选, 去蹭了一节发现讲的内容虽然我不懂但是还是无爱. 我想既然都本科就出来工作还是按照 ...

  9. ubuntu14 安装和使用docker

    Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互 ...

  10. Dinic 算法钩沉

    最初是从<挑战程序设计竞赛>上了解到 Dinic 算法的.其中对于 Dinic 算法中的关键词--分层图(layered network,也称『层次图』)的引入的解释如下: 因为最短增广路 ...