[BZOJ 3489] A simple rmq problem 【可持久化树套树】
题目链接:BZOJ - 3489
题目分析
“因为是OJ上的题,就简单点好了。”——出题人
真的..好..简单...
首先,我们求出每个数的前一个与它相同的数的位置,即 prev[i] ,如果前面没有相同的数,prev[i] = 0。
再求出每个数的后一个与它相同的数的位置,即 next[i], 如果后面没有相同的数,next[i] = n + 1。
这样,对于 l > prev[i], r < next[i] 的区间,i 这个数在区间中至多出现一次。
那么我们要求的就是:符合 prev[i] < l, next[i] > r, l <= i <= r 的最大的 value[i] 。
这样我们可以用可持久化树套树来做。
将所有的数按照 prev[i] 从小到大排序,然后按照这个顺序建立外层的可持久化线段树,每棵可持久化线段树的节点代表的是 next[i] 的区间,每个节点都是一颗可持久化线段树,内层可持久化线段树的节点代表的是 i 的区间,即数在原数列中的位置。
每次 Insert 一个 i ,是在前一棵可持久化线段树的基础上根据这个 i 的 next[i] 增加一条链,然后链上的 logn 个节点都要更新一下它们的内层可持久化线段树,每棵内层线段树也是在之前的内层可持久化线段树的基础上新建一条链,这样总的空间复杂度是 O(n log^2n) 的。
询问 (l, r) 的时候,二分找到一个最大的 p ,满足 prev[p] < l,然后在 p 的外层可持久化线段树中查询 [r + 1, n + 1],即 next > r ,在这个区间的内层可持久化线段树中查找在 [l, r] 区间中的最大值。
时间复杂度也是 O(n log^2n) 的。
代码
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm> using namespace std; inline void Read(int &Num)
{
char c = getchar();
bool Neg = false;
while (c < '0' || c > '9')
{
if (c == '-') Neg = true;
c = getchar();
}
Num = c - '0'; c = getchar();
while (c >= '0' && c <= '9')
{
Num = Num * 10 + c - '0';
c = getchar();
}
if (Neg) Num = -Num;
} inline int gmin(int a, int b) {return a < b ? a : b;}
inline int gmax(int a, int b) {return a > b ? a : b;} const int MaxN = 100000 + 5, MaxNodeI = 2000000 + 5, MaxNodeII = 40000000 + 5; int n, m, Ans, IndexI, IndexII;
int Last[MaxN], Root_I[MaxN], Son_I[MaxNodeI][2], Root_II[MaxNodeI], Son_II[MaxNodeII][2], T[MaxNodeII]; struct ES
{
int Pos, Num, Prev, Next; bool operator < (const ES &b) const
{
return Prev < b.Prev;
}
bool operator < (const int &b) const
{
return Prev < b;
}
} E[MaxN]; void Insert_II(int &x, int Last, int s, int t, int Pos, int Num)
{
if (x == 0) x = ++IndexII;
T[x] = gmax(T[Last], Num);
if (s == t) return;
int m = (s + t) >> 1;
if (Pos <= m)
{
Son_II[x][1] = Son_II[Last][1];
Insert_II(Son_II[x][0], Son_II[Last][0], s, m, Pos, Num);
}
else
{
Son_II[x][0] = Son_II[Last][0];
Insert_II(Son_II[x][1], Son_II[Last][1], m + 1, t, Pos, Num);
}
} void Insert_I(int &x, int Last, int s, int t, int Nxt, int Pos, int Num)
{
if (x == 0) x = ++IndexI;
Insert_II(Root_II[x], Root_II[Last], 0, n + 1, Pos, Num);
if (s == t) return;
int m = (s + t) >> 1;
if (Nxt <= m)
{
Son_I[x][1] = Son_I[Last][1];
Insert_I(Son_I[x][0], Son_I[Last][0], s, m, Nxt, Pos, Num);
}
else
{
Son_I[x][0] = Son_I[Last][0];
Insert_I(Son_I[x][1], Son_I[Last][1], m + 1, t, Nxt, Pos, Num);
}
} int Get_II(int x, int s, int t, int l, int r)
{
if (x == 0) return 0;
if (l <= s && r >= t) return T[x];
int ret = 0, m = (s + t) >> 1;
if (l <= m) ret = gmax(ret, Get_II(Son_II[x][0], s, m, l, r));
if (r >= m + 1) ret = gmax(ret, Get_II(Son_II[x][1], m + 1, t, l, r));
return ret;
} int Get_I(int x, int s, int t, int l_I, int r_I, int l_II, int r_II)
{
if (x == 0) return 0;
if (l_I <= s && r_I >= t) return Get_II(Root_II[x], 0, n + 1, l_II, r_II);
int ret = 0, m = (s + t) >> 1;
if (l_I <= m) ret = gmax(ret, Get_I(Son_I[x][0], s, m, l_I, r_I, l_II, r_II));
if (r_I >= m + 1) ret = gmax(ret, Get_I(Son_I[x][1], m + 1, t, l_I, r_I, l_II, r_II));
return ret;
} int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
Read(E[i].Num);
E[i].Pos = i;
E[i].Prev = Last[E[i].Num];
E[E[i].Prev].Next = i;
E[i].Next = n + 1;
Last[E[i].Num] = i;
}
sort(E + 1, E + n + 1);
for (int i = 1; i <= n; ++i)
Insert_I(Root_I[i], Root_I[i - 1], 0, n + 1, E[i].Next, E[i].Pos, E[i].Num);
Ans = 0;
int x, y, l, r, p;
for (int i = 1; i <= m; ++i)
{
Read(x); Read(y);
l = gmin((x + Ans) % n + 1, (y + Ans) % n + 1);
r = gmax((x + Ans) % n + 1, (y + Ans) % n + 1);
p = lower_bound(E + 1, E + n + 1, l) - E - 1;
Ans = Get_I(Root_I[p], 0, n + 1, r + 1, n + 1, l, r);
printf("%d\n", Ans);
}
return 0;
}
[BZOJ 3489] A simple rmq problem 【可持久化树套树】的更多相关文章
- BZOJ 3489 A simple rmq problem(可持久化线段树)
题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3489 题意:一个数列.每次询问一个区间内出现一次的最大的数字是多少. 思路:设la ...
- BZOJ 3489 A simple rmq problem 可持久化KDtree/二维线段树
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题意概述: 给出一个序列,每次询问一个序列区间中仅出现了一次的数字最大是多少,如果 ...
- bzoj 3489: A simple rmq problem k-d树思想大暴力
3489: A simple rmq problem Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 551 Solved: 170[Submit][ ...
- BZOJ 3489: A simple rmq problem
3489: A simple rmq problem Time Limit: 40 Sec Memory Limit: 600 MBSubmit: 1594 Solved: 520[Submit] ...
- bzoj 3489 A simple rmq problem——主席树套线段树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...
- BZOJ 3489: A simple rmq problem (KD-tree做法)
KD树水过这道可持久化树套树-其实就是个三维偏序 题解戳这里 CODE #include <bits/stdc++.h> using namespace std; #define ls ( ...
- bzoj 3489 A simple rmq problem - 线段树
Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...
- bzoj 3489 A simple rmq problem —— 主席树套线段树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...
- BZOJ.3489.A simple rmq problem(主席树 Heap)
题目链接 当时没用markdown写,可能看起来比较难受...可以复制到别的地方看比如DevC++. \(Description\) 给定一个长为n的序列,多次询问[l,r]中最大的只出现一次的数.强 ...
随机推荐
- im消息丢失插件
https://github.com/laughin/mocamsg mocamsg Moca message interceptor Openfire网络不好的情况下经常丢消息,一般情况都是服务器端 ...
- 在android客户端加载html源代码总结
在实际应用中,客户端要从网页上获取数据是常见的事,如果要解析网页上的html文档,那么首先得获取html源码,然后现在一般使用Jsoup来转换成Document文档来进行解析,本文主要讨论如何使用Js ...
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android ...
- oracle13 触发器 变量
触发器 触发器是指隐含的执行的存储过程.当定义触发器时,必须要指定触发的事件和触发的操作,常用的触发事件包括insert,update,delete语句,而触发操作实际就是一个pl/sql块.可以 ...
- hdu2035java
人见人爱A^B Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Sub ...
- GUI编程笔记(java)01:GUI和CLI
GUI Graphical User Interface(图形用户接口). 用图形的方式,来显示计算机操作的界面,这样更方便更直观. CLI Command line User Interface ( ...
- 15个Linux Yum命令实例--安装/卸载/更新
在linux环境中, 安装, 卸载更新包是一个极为常见的操作.大部分的linux环境都提供了包的管理工具, 例如:apt-get, dpkg, rpm, yum等等. 一些Linux环境中,yum是默 ...
- JVM体系结构
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/5187049.html ...
- Plain old data structure(POD)
Plain old data structure, 缩写为POD, 是C++语言的标准中定义的一类数据结构,POD适用于需要明确的数据底层操作的系统中.POD通常被用在系统的边界处,即指不同系统之间只 ...
- android获取Mac地址和IP地址
获取Mac地址实际项目中测试了如下几种方法:(1)设备开通Wifi连接,获取到网卡的MAC地址(但是不开通wifi,这种方法获取不到Mac地址,这种方法也是网络上使用的最多的方法) //根据Wifi信 ...