【31.94%】【BZOJ 3489】A simple rmq problem

Time Limit: 40 Sec  Memory Limit: 600 MB
Submit: 1434  Solved: 458
[Submit][Status][Discuss]

Description

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

 

Input

第一行为两个整数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

Output

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

Sample Input

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

Sample Output

4
10
10
0
0
10
0
4
0
4

HINT



注意出题人为了方便,input的第二行最后多了个空格。


2015.6.24新加数据一组,2016.7.9放至40S,600M,但未重测


 

Source


【题解】

这题如果用code forces中44G的遍历kd-tree方法来做。会超时。

说下思路。

设[l,r]内不重复的数字的下标为x;

则设pre[x],last[x]为x上一次出现的下标位置,和下一次出现的下标位置。

如果之前没有出现过则为0,如果后面没出现过则为n+1;

则pre[x]<l 且 last[x] >r,且x∈[l,r]

则以(x,pre[x],last[x])作为一个3维的点。

我们如果输入为(l,r)。则要找到一个点 l <= x <= r,pre[x] <l,last[x] > r;

即寻找某个范围内的点。然后要求key值是最大的而已。

【代码】

#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 109000;

struct point
{
	int d[3], l, r, max, n,ma_x[3],mi_n[3];
};

int n, m, pre[MAXN] = { 0 }, last[MAXN], root, now, mi_n, ma_x,ans = 0;
point t[MAXN];

void input_data()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &t[i].n);
		t[i].d[0] = i;
		t[i].d[1] = pre[t[i].n];
		pre[t[i].n] = i;
	}
	for (int i = n; i >= 1; i--)
		last[t[i].n] = n + 1;
	for (int i = n; i >= 1; i--)
	{
		int temp = t[i].n;
		t[i].d[2] = last[temp];
		last[temp] = i;
	}
}

bool cmp(point a, point b)
{
	return a.d[now] < b.d[now];
}

void up_data(int rt)
{
	int l = t[rt].l, r = t[rt].r;
	if (l)
	{
		for (int i = 0; i <= 2; i++)
		{
			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
		}
		t[rt].max = max(t[rt].max, t[l].max); 
	}
	if (r)
	{
		for (int i = 0; i <= 2; i++)
		{
			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
		}
		t[rt].max = max(t[rt].max, t[r].max);
	}
}

int build(int begin, int end, int fx)
{
	int m = (begin + end) >> 1;
	now = fx;
	nth_element(t + begin, t + m, t + end + 1, cmp);
	t[m].max = t[m].n;//max最后保存的是这棵子树的key值的最大值。
	for (int i = 0; i <= 2; i++)//这是这棵树中最小最大的3维坐标。
		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
	if (begin < m)
		t[m].l = build(begin, m - 1, (fx + 1) % 3);
	if (m < end)
		t[m].r = build(m + 1, end, (fx + 1) % 3);
	up_data(m);
	return m;
}

void get_ans()
{
	root = build(1, n, 0);
}

bool ok(int rt) //判断这个子树有没有可能有解
{
	if (t[rt].ma_x[0] < mi_n || t[rt].mi_n[0] > ma_x)
		return 0;
	if (t[rt].mi_n[1] >= mi_n || t[rt].ma_x[2] <= ma_x)
		return 0;
	return 1;
}

void query(int rt)
{
	if (rt == 0)
		return;
	if (mi_n <= t[rt].mi_n[0] && t[rt].ma_x[0] <= ma_x && t[rt].ma_x[1] < mi_n
		&& t[rt].mi_n[2] > ma_x) //这是整个子树都可以作为解的情况
	{
		ans = max(ans, t[rt].max);
		return;
	}
	if (mi_n <= t[rt].d[0] && t[rt].d[0] <= ma_x && t[rt].d[1] < mi_n
		&& t[rt].d[2] > ma_x)//这个则是单个节点。
		ans = max(ans, t[rt].n);
	int l = t[rt].l, r = t[rt].r;
	if (t[l].max > t[r].max)//往下走。
	{
		if (t[l].max > ans && ok(l))
			query(l);
		if (t[r].max > ans && ok(r))
			query(r);
	}
	else
	{
		if (t[r].max > ans && ok(r))
			query(r);
		if (t[l].max > ans && ok(l))
			query(l);
	}
}

void output_ans()
{
	int la = 0;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		mi_n = min(((x + la) % n) + 1, ((y + la) % n) + 1);
		ma_x = max(((x + la) % n) + 1, ((y + la) % n) + 1);
		ans = 0;
		query(root);
		printf("%d\n", ans);
		la = ans;
	}
}

int main()
{
	//freopen("F:\\rush.txt", "r", stdin);
	input_data();
	get_ans();
	output_ans();
	return 0;
}



posted @ 2017-10-06 19:23  AWCXV  阅读(147)  评论(0编辑  收藏  举报