[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]中最大的只出现一次的数.强 ...
随机推荐
- Android自定义属性时TypedArray的使用方法
有时候android传统的页面布局不足以满足我们的需求,常常需要自己定义view,通常继承View,然后重写构造方法以及onDraw等函数,再 具体实现自己定义的复杂view.我们知道在给控件赋属性时 ...
- 文件IO 练习题
3.1 当读/写磁盘文件时,本章中描述的函数是否具有缓冲机制?请说明原因. 3.1 所有的磁盘 I/O 都要经过内核的块缓冲区(也称为内核的缓冲区高速缓存),唯一例 外的是对原始磁盘设备的 I/O,但 ...
- hibernate之.hbm.xml文件内容相关参数说明
1.<id> 表示主键,下面介绍主键的生成机制 Increment: 由hibernate自动以递增的方式生成表识符,每次增量为1 Identity:由底层数据库生成表识符.条件是数据库支 ...
- CocoaPods导入第三方库头文件自动补齐
使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...
- asp.net 使用IHttpModule 做权限检查 登录超时检查(转)
IHttpModule 权限 检查 登录超时检查 这样就不需要每个页面都做一次检查 也不需要继承任何父类. using System;using System.Collections.Generic; ...
- php curl_init函数用法
原文地址:curl_init函数用法">php curl_init函数用法作者:loading 使用PHP的cURL库可以简单和有效地去抓网页.你只需要运行一个脚本,然后分析一下你所抓 ...
- jquery 提示信息显示后自动消失的具体实现
方法一: 复制代码 代码如下: $("#errormsg").html("您的信息输入错误,请重试!").show(300).delay(3000).hide( ...
- Android - 广播机制和Service
花了几天,想实现定位功能,使用的是百度的服务,有时真的很无奈,就是一小小个问题,就能折腾好几天. 首先,我是在主线程(Fragment)中单独进行定位,发现不起作用. 然后我想使用Service和广播 ...
- Java file文件的写入和读取及下载
File文件的写入 一.FileWriter 和BufferedWriter 结合写入文件 FileWriter是字符流写入字符到文件.默认情况下,它会使用新的内容代替文件原有的所有内容,但是,当指定 ...
- 判断ios是app第一次启动
首次运行的应用程序加入一些help 或者 宣传动画 现在变的很重要了. 一个有用的例子是发送一个分析实例.这可能是一个很好的方法来确定有多少人下载实用应用程序.有人会说:“但是,嘿,苹果AppStor ...