3489: A simple rmq problem
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 326 Solved: 85
[ Submit][ Status]
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
Solution
这个题我们可以:“矩阵主席树”。
=_= shen me gui =_= shen me gui =_= shen me gui =_= shen me gui =_=
我们考虑对于序列中的一个元素 x:
设它的前驱为 a,若没有前驱则视为 0,
设它的后继为 b,若没有后继则视为 n + 1。
然后对于左端点在 [a + 1, x],右端点在 [x, b - 1] 中的区间,
都会只包含这个元素 1 次。
于是我们考虑设置一个矩阵。
第一维是左端点,第二维是右端点,
矩阵中的每个元素就代表一个区间。
于是我们考虑给每一个值维护一个矩阵。
然而,真的有必要吗?
我们可以记录一个“后缀矩阵”。
即矩阵 i 就代表着把 [i, n] 中的所有矩阵给合并起来,
也就是有 Suffix_Matrix[i][j][k] = or_sum(Matrix[t][j][k]) (t = i to n)。
然后这个东西可以用主席树来维护嘛。
然后询问的时候就二分答案单点查询就可以了。
由于单次矩阵赋值操作的复杂度是 O(log^2 n) 的。
并且每个元素只会对操作次数产生 1 的贡献。
所以操作部分的复杂度是 O(n log^2 n) 的。
单点查询的复杂度是 O(log n) 的,
然后对于每个询问我们要二分答案,
于是单次处理询问的复杂度也是 O(log^2 n) 的。
于是回答询问的复杂度是 O(m log^2 n) 的。
所以总时间复杂度就是 O((n + m) log^2 n) 的。
然后我们可以把标记永久化,即不下传,
反正值只会是 0 或者 1,并且只可能从 0 变 1 而不会从 1 变 0。
所以你整个矩阵都是 1 了单点还难道不是 1 吗?
所以就没必要下传标记了。
这可以大大节省空间。
然后这样优化之后才能过掉这个题。。。
于是我亲切地把这货叫做“矩阵主席树”=_=
毕竟 Gromah 太弱,只会做水题。
=_= shen me gui =_= shen me gui =_= shen me gui =_= shen me gui =_=
我们考虑对于序列中的一个元素 x:
设它的前驱为 a,若没有前驱则视为 0,
设它的后继为 b,若没有后继则视为 n + 1。
然后对于左端点在 [a + 1, x],右端点在 [x, b - 1] 中的区间,
都会只包含这个元素 1 次。
于是我们考虑设置一个矩阵。
第一维是左端点,第二维是右端点,
矩阵中的每个元素就代表一个区间。
于是我们考虑给每一个值维护一个矩阵。
然而,真的有必要吗?
我们可以记录一个“后缀矩阵”。
即矩阵 i 就代表着把 [i, n] 中的所有矩阵给合并起来,
也就是有 Suffix_Matrix[i][j][k] = or_sum(Matrix[t][j][k]) (t = i to n)。
然后这个东西可以用主席树来维护嘛。
然后询问的时候就二分答案单点查询就可以了。
由于单次矩阵赋值操作的复杂度是 O(log^2 n) 的。
并且每个元素只会对操作次数产生 1 的贡献。
所以操作部分的复杂度是 O(n log^2 n) 的。
单点查询的复杂度是 O(log n) 的,
然后对于每个询问我们要二分答案,
于是单次处理询问的复杂度也是 O(log^2 n) 的。
于是回答询问的复杂度是 O(m log^2 n) 的。
所以总时间复杂度就是 O((n + m) log^2 n) 的。
然后我们可以把标记永久化,即不下传,
反正值只会是 0 或者 1,并且只可能从 0 变 1 而不会从 1 变 0。
所以你整个矩阵都是 1 了单点还难道不是 1 吗?
所以就没必要下传标记了。
这可以大大节省空间。
然后这样优化之后才能过掉这个题。。。
于是我亲切地把这货叫做“矩阵主席树”=_=
毕竟 Gromah 太弱,只会做水题。
Gromah's Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 300000 + 5
#define M 42000000 + 5
int n, m, tot, cnt, Head[N], Root[N];
struct Chain
{
int next, node;
}E[N];
struct Node
{
int l, r;
bool d, flag;
}h[M];
inline int getint()
{
char ch = '\n';
for (; ch > '9' || ch < '0'; ch = getchar()) ;
int res = ch - '0';
for (ch = getchar(); ch >= '0' && ch <= '9'; ch = getchar())
res = (res << 3) + (res << 1) + ch - '0';
return res;
}
inline void addedge(int u, int v)
{
E[++ tot].next = Head[u];
Head[u] = tot;
E[tot].node = v;
}
inline int Inc(int a, int b)
{
return a + b - (a + b >= n ? n : 0) + 1;
}
inline void update(int x)
{
h[x].flag = h[h[x].l].flag | h[h[x].r].flag;
}
inline void apply(int x)
{
h[x].d = h[x].flag = 1;
}
inline void Modify(int &x, int t, int b, int l, int r, int st, int sb, int sl, int sr)
{
h[++ cnt] = h[x];
x = cnt;
if (t == st && b == sb && l == sl && r == sr)
{
apply(x);
return ;
}
if (h[x].d) return ;
if (b - t >= r - l)
{
int mid = t + b >> 1;
if (sb <= mid) Modify(h[x].l, t, mid, l, r, st, sb, sl, sr);
else if (st > mid) Modify(h[x].r, mid + 1, b, l, r, st, sb, sl, sr);
else Modify(h[x].l, t, mid, l, r, st, mid, sl, sr), Modify(h[x].r, mid + 1, b, l, r, mid + 1, sb, sl, sr);
}
else if (b - t < r - l)
{
int mid = l + r >> 1;
if (sr <= mid) Modify(h[x].l, t, b, l, mid, st, sb, sl, sr);
else if (sl > mid) Modify(h[x].r, t, b, mid + 1, r, st, sb, sl, sr);
else Modify(h[x].l, t, b, l, mid, st, sb, sl, mid), Modify(h[x].r, t, b, mid + 1, r, st, sb, mid + 1, sr);
}
update(x);
}
inline bool Query(int x, int t, int b, int l, int r, int u, int v)
{
if (!h[x].flag) return 0;
if (h[x].d) return 1;
if (t == b && l == r)
return h[x].flag;
if (b - t >= r - l)
{
int mid = t + b >> 1;
if (u <= mid) return Query(h[x].l, t, mid, l, r, u, v);
else return Query(h[x].r, mid + 1, b, l, r, u, v);
}
else if (b - t < r - l)
{
int mid = l + r >> 1;
if (v <= mid) return Query(h[x].l, t, b, l, mid, u, v);
else return Query(h[x].r, t, b, mid + 1, r, u, v);
}
}
int main()
{
n = getint(), m = getint();
for (int i = 1; i <= n; i ++)
addedge(i, 0);
for (int i = 1; i <= n; i ++)
addedge(getint(), i);
for (int i = 1; i <= n; i ++)
addedge(i, n + 1);
for (int i = n; i; i --)
{
int a, b, c;
a = E[Head[i]].node;
b = E[Head[i] = E[Head[i]].next].node;
Root[i] = Root[i + 1];
while (E[Head[i]].next)
{
Head[i] = E[Head[i]].next;
int c = E[Head[i]].node;
Modify(Root[i], 1, n, 1, n, c + 1, b, b, a - 1);
a = b, b = c;
}
}
int last = 0;
while (m --)
{
int x = Inc(getint(), last), y = Inc(getint(), last), l = 0, r = n;
if (x > y) swap(x, y);
while (l < r)
{
int mid = l + r + 1 >> 1;
if (Query(Root[mid], 1, n, 1, n, x, y))
l = mid;
else r = mid - 1;
}
printf("%d\n", last = l);
}
return 0;
}