[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;
}

  

时间: 2024-10-11 12:45:22

[BZOJ 3489] A simple rmq problem 【可持久化树套树】的相关文章

BZOJ 3489 A simple rmq problem 可持久化树套树

题目大意:给定一个序列,多次询问某一区间中出现且仅出现一次的最大的数 令第i个数左侧第一个与这个数相同的数为last[i] 右侧第一个与这个相同的数为next[i] 那么一个数a[i]在区间内出现一次当且仅当last[i]<l&&next[i]>r&&l<=i<=r 于是我们将元素按照last[i]排序并构建可持久化线段树 令pos为满足last[i]<l的最大的i 每次查询我要查询的是第pos个版本的线段树内所有next[i]>r的数中

bzoj 3489 A simple rmq problem - 线段树

Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每行两个整数x,y, 询问区间[l,r]由下列规则产生(OIER

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9bc46b690756fe252e17fc3ca90aa01.html 在我挣扎一下午时 Narh 早就A了... 于是看看有何不同,发现 add  和 insert 中必须把 ls[x] = ls[y] , rs[x] = rs[y] 写在前面,而不能是修改 rs 则在那里单写一个 ls[x] =

bzoj 3489: A simple rmq problem

1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define M 200009 5 using namespace std; 6 struct A 7 { 8 int d[3],mx[3],mn[3],l,r,v,mx1; 9 }a[M]; 10 int n,b[M],v[M],N,ans,root,m; 11 bool cmp(A a1,A a2) 12 { 13 return a1.d

bzoj3489 A simple rmq problem 可持久化树套树

先预处理出两个个数组pre,next.pre[i]表示上一个与i位置数字相同的位置,若不存在则设为0:next[i]表示下一个与i位置数字相同的位置,若不存在则设为n+1.那么一个满足在区间[L,R]中只出现一次的数字,其pre[i]<L,next[i]>R. 这样我们可以先将pre进行排序,然后将pre可持久化,外层线段树套的是当前数字的位置i,内层线段树套的是next[i].外层线段树的节点总数是nlogn,内层线段树节点总数是nlogn^2.时间复杂度O(nlogn^2). 代码 1 #

【BZOJ】【3489】A simple rmq problem

KD-Tree(乱搞) Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足$ ( pre[i]<ql \ and \ nex[i]>qr\ and\ i \in [ql,qr] ) $ 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路

[bzoj3489]A simple rmq problem

本题既不是rmq也不会simple(对我这种蒟蒻而言) 一开始只能想到树套树套树TAT然后看了看数据范围果断滚去膜拜题解. 然后才知道预先排序一下可以弄掉一个log.不过得写可持久化线段树套可持久化线段树.. 然后愉悦的开码了...感人的是竟然不用调...更感人的是交上去直接tle了. 然后从网上找了别人的代码(方法一样)发现同样的数据我要跑6s+..标称只要2s+.. 之后各种卡常还是慢了一倍TAT...最后自己写个max函数就和标程一样快了TAT这几天怎么总是出些奇怪的状况QAQ. 本来故事

【BZOJ3489】A simple rmq problem kd-tree

[BZOJ3489]A simple rmq problem Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每

【kd-tree】bzoj3489 A simple rmq problem

Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足(pre[i]<ql and nex[i]>qr and i∈[ql,qr]) 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路不由自主又代入到搞[子树最大值]来更新答案……然而忘记了