题目描述

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. 剑指Offer(书):顺时针打印数组

    题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1 ...

  2. LeetCode(217)Contains Duplicate

    题目 Given an array of integers, find if the array contains any duplicates. Your function should retur ...

  3. LeetCode(289)Game of Life

    题目 According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cel ...

  4. MDK editions for Nuvoton devices

    10 Sep 2018 MDK editions for Nuvoton devices For users of Nuvoton devices, Keil® MDK increases its a ...

  5. 欧拉函数:HDU1787-GCD Again(欧拉函数的模板)

    GCD Again Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  6. 官网下载MySQL

    1)首先我们访问MySQL官网https://dev.mysql.com/,然后如下 2)我们向下拉取滚动条,来到如下界面,选择Source Code 3)向下拉取滚动条,来到如下界面,操作如下: 4 ...

  7. 系统测试过程截获SQL方法

    1      摘要 测试过程中,经常会遇到莫名的各种问题,可能从开发同学的日志无法发现具体出现问题的原因,本着测试同学深入分析.定位问题的目的,经常需要一些额外的手段获得更多的错误异常信息. 我们涉及 ...

  8. Educational Codeforces Round 2 Edge coloring of bipartite graph

    题意: 输入一个二分图,用最少的颜色数给它的每条边染色,使得同一个顶点连的边中颜色互不相同. 输出至少需要的颜色数和任意一种染色方案. 分析: 证明不会,只说一下(偷瞄巨巨代码学到的)做法. 假设点的 ...

  9. c++ dll 创建

    建立一个C++的Win32DLL,这里要注意选择"Export symbols"导出符号.点击完成. 如下图所示:   由于项目的名称是"TestCPPDLL" ...

  10. 基于百度OCR的图片文字识别

    先上图,有图有真相 首先在百度开通ORC服务,目前是免费的,普通识别每天50000次免费,非常棒! 百度文档:http://ai.baidu.com/docs#/OCR-API/top 下载百度SDK ...