【31.94%】【BZOJ 3489】A simple rmq problem
Submit: 1434 Solved: 458
[Submit][Status][Discuss]
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都知道是怎样的吧>_<):
l=min((x+lastans)mod n+1,(y+lastans)mod n+1);
r=max((x+lastans)mod n+1,(y+lastans)mod n+1);
Lastans表示上一个询问的答案,一开始lastans为0
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
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
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; }