【bzoj3489】A simple rmq problem 三维KD-tree

题目描述

因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。

输入

第一行为两个整数N,MM是询问数,N是序列的长度(N<=100000M<=200000)

第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N

再下面M行,每行两个整数xy

询问区间[l,r]由下列规则产生(OIER都知道是怎样的吧>_<)

l=min(x+lastans)mod n+1,(y+lastansmod n+1);

r=max(x+lastans)mod n+1,(y+lastansmod n+1);

Lastans表示上一个询问的答案,一开始lastans0

输出

一共M行,每行给出每个询问的答案。

样例输入

10 10
6 4 9 10 9 10 9 4 10 4
3 8
10 1
3 4
9 4
8 1
7 8
2 9
1 1
7 3
9 9

样例输出

4
10
10
0
0
10
0
4
0
4


题解

三维KD-tree

一个数在区间[l,r]内仅出现过一次,当且仅当:它在[l,r]之间,它的上一个位置在[0,l-1]之间,它的下一个位置在[r+1,n+1]之间。

于是我们可以处理出一个数的上一个出现位置和下一个出现位置,把它的位置与这两个位置放在一起,看作三维空间的点。每次查询就是查区域内点的权值的最大值。

用三维KD-tree维护一下就好。

注意剪枝,如果一个空间内的点的权值的最大值<=ans,那么直接跳出。

再也不丧心病狂的把cmp函数写错了QAQ

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define rep for(int i = 0 ; i < 3 ; i ++ )
using namespace std;
int v[N] , last[N] , next[N] , pos[N] , d , root , l , r , ans;
struct data
{
	int p[3] , maxn[3] , minn[3] , w , val , c[2];
	bool operator <(data b)const
	{
		return p[d] == b.p[d] ? p[(d + 1) % 3] == b.p[(d + 1) % 3] ? p[(d + 2) % 3] < b.p [(d + 2) % 3] : p[(d + 1) % 3] < b.p[(d + 1) % 3] : p[d] < b.p[d];
	}
}a[N];
inline int read()
{
	int ret = 0; char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = getchar();
	return ret;
}
void pushup(int x , int k)
{
	rep a[x].maxn[i] = max(a[x].maxn[i] , a[k].maxn[i]) , a[x].minn[i] = min(a[x].minn[i] , a[k].minn[i]);
	a[x].val = max(a[x].val , a[k].val);
}
int build(int l , int r , int now)
{
	int mid = (l + r) >> 1;
	d = now , nth_element(a + l , a + mid , a + r + 1);
	rep a[mid].maxn[i] = a[mid].minn[i] = a[mid].p[i];
	a[mid].val = a[mid].w;
	if(l < mid) a[mid].c[0] = build(l , mid - 1 , (now + 1) % 3) , pushup(mid , a[mid].c[0]);
	if(r > mid) a[mid].c[1] = build(mid + 1 , r , (now + 1) % 3) , pushup(mid , a[mid].c[1]);
	return mid;
}
bool judge(int x)
{
	return a[x].val > ans && a[x].maxn[0] >= l && a[x].minn[0] <= r && a[x].minn[1] < l && a[x].maxn[2] > r;
}
void query(int x)
{
	if(!x || !judge(x)) return;
	if(a[x].p[0] >= l && a[x].p[0] <= r && a[x].p[1] < l && a[x].p[2] > r) ans = max(ans , a[x].w);
	query(a[x].c[0]) , query(a[x].c[1]);
}
int main()
{
	int n , m , i;
	n = read() , m = read();
	for(i = 1 ; i <= n ; i ++ ) v[i] = read() , last[i] = pos[v[i]] , next[pos[v[i]]] = i , pos[v[i]] = i;
	for(i = 1 ; i <= n ; i ++ ) a[i].p[0] = i , a[i].p[1] = last[i] , a[i].p[2] = next[i] ? next[i] : n + 1 , a[i].w = v[i];
	root = build(1 , n , 0);
	while(m -- )
	{
		l = read() , r = read() , l = (l + ans) % n + 1 , r = (r + ans) % n + 1;
		if(l > r) swap(l , r);
		ans = 0 , query(root) , printf("%d\n" , ans);
	}
	return 0;
}

 

 

posted @ 2017-07-04 10:50  GXZlegend  阅读(509)  评论(0编辑  收藏  举报