题目链接: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 【可持久化树套树】的更多相关文章

  1. BZOJ 3489 A simple rmq problem(可持久化线段树)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3489 题意:一个数列.每次询问一个区间内出现一次的最大的数字是多少. 思路:设la ...

  2. BZOJ 3489 A simple rmq problem 可持久化KDtree/二维线段树

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题意概述: 给出一个序列,每次询问一个序列区间中仅出现了一次的数字最大是多少,如果 ...

  3. 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][ ...

  4. BZOJ 3489: A simple rmq problem

    3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 600 MBSubmit: 1594  Solved: 520[Submit] ...

  5. bzoj 3489 A simple rmq problem——主席树套线段树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...

  6. BZOJ 3489: A simple rmq problem (KD-tree做法)

    KD树水过这道可持久化树套树-其实就是个三维偏序 题解戳这里 CODE #include <bits/stdc++.h> using namespace std; #define ls ( ...

  7. bzoj 3489 A simple rmq problem - 线段树

    Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直 ...

  8. bzoj 3489 A simple rmq problem —— 主席树套线段树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...

  9. BZOJ.3489.A simple rmq problem(主席树 Heap)

    题目链接 当时没用markdown写,可能看起来比较难受...可以复制到别的地方看比如DevC++. \(Description\) 给定一个长为n的序列,多次询问[l,r]中最大的只出现一次的数.强 ...

随机推荐

  1. 单片机IO口驱动能力

    以STM32的IO口为例,最大的输出电流和灌入电流在芯片手册上都有说明.单个IO口一般都是十几mA到几十mA,同时总的VDD电流也有限制,大概为150mA.所以单片机驱动外设时,如果不是信号型而是功率 ...

  2. Swift 析构器deinit

    析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用.析构器用关键字deinit来标识,类似于构造器用init来标识. 原理: Swift会自动释放不再需要的实例以释放资源.Swift通 ...

  3. Linux系统下UDP发送和接收广播消息小样例

    [cpp] view plaincopy // 发送端 #include <iostream> #include <stdio.h> #include <sys/sock ...

  4. pugixml

    http://www.firedragonpzy.com.cn/index.php/archives/3227 有关cocos2d-x的xml文件读取问题

  5. 关于js中伪数组

    伪数组: 具有length属性: 按索引方式存储数据: 不具有数组的push().pop()等方法: 伪数组无法直接调用数组方法或期望length属性有什么特殊的行为,不具有数组的push().pop ...

  6. android js 互调

    public class BoatsActivity extends Activity { Handler mHandler = new Handler();//处理消息的handler @Suppr ...

  7. C# 序列化和反序列

    1.对象序列化的介绍 (1).NET支持对象序列化的几种方式 二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Seri ...

  8. C#选择文件、选择文件夹、打开文件(或者文件夹)

    1.选择文件用OpenDialog OpenFileDialog dialog = new OpenFileDialog(); dialog.Multiselect = true;//该值确定是否可以 ...

  9. 关于css的兼容

    这篇随笔为了方便自己后期的学习和查找,用来记录平时遇到的一些问题,后期会陆续更新 1.背景图 :background-position属性,在ff下不支持该属性的拆分写法(background-pos ...

  10. mysql命令使用

    1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...