题意
- 注意L可能大于R(应该都注意到了)
题解
- 对于固定的左端点或者右端点,不同的值至多只有log个
- 先把这些值找出来,然后去掉具有包含关系的相同的值
- 单纯的二维偏序求和无法处理相同的值
- 那么把相同的值按顺序放在一起 (l1,r1)(l2,r2)...(lp,rp)
- 那么如果查询包含了(l1,r2)(l2,r3)...(lp-1,rp)这些区间,答案就应该-1
- 所以设置一些权值为-1的区间,再二维偏序即可
总结
对于一般的(离线)二维偏序问题,常常对第一维排序后,用树状数组维护第二维的值。
而这道题中,需要解决的是强制在线的二维偏序问题,就需要用到主席树处理区间。
多个根结点rt[i]代表不同的右区间,通过继承上一个根结点的信息,保证了其内部包括的区间的第二维只会是递增的。
主席树此时等价于n棵线段树,查询操作相当于询问左区间个数之和,时间复杂度是log(n)的。
代码
#include <iostream>
#include <map>
using namespace std;
const int N = 1e5 + 5;
#define mid ((l+r)>>1)
#define lson tr[o].l, l, mid
#define rson tr[o].r, mid+1, r
struct node {
int sum, l, r;
}tr[N*400];
int tot = 0, rt[N]; // 空间开32倍RE了
void update(int &o, int l, int r, int i, int v) {
tr[++tot] = tr[o]; o = tot; tr[o].sum += v; // 继承上一个右区间的信息后,更新点权
if(l == r) return;
else if(i <= mid) update(lson, i, v);
else update(rson, i, v);
}
int query(int o, int l, int r, int L, int R) {
if(l > R || r < L) return 0;
if(L <= l && r <= R) return tr[o].sum;
return query(lson, L, R) + query(rson, L, R);
}
int main() {
int n; scanf("%d", &n);
map<int, int> pre, his;
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
map<int, int> cur;
pre[(1<<30)-1] = i; // 考虑当前节点构成区间
for (auto o : pre) {
int key = o.first & x;
cur[key] = max(cur[key], o.second);
}
rt[i] = rt[i-1]; // 继承上一个右区间的信息
for (auto o : cur) {
if(his.count(o.first)) {
update(rt[i], 1, n, his[o.first], -1); // 去重
}
his[o.first] = o.second;
update(rt[i], 1, n, o.second, 1);
}
swap(cur, pre);
}
int q;
scanf("%d", &q);
int last = 0;
while (q--) {
int l, r;
scanf("%d %d", &l, &r);
l = (l ^ last) % n + 1;
r = (r ^ last) % n + 1;
if(l > r) swap(l, r);
last = query(rt[r], 1, n, l, r);
printf("%d
", last);
}
}