题目链接
- 题意:给一个长度为 n n n 的排列,m次询问区间 [ l , r ] [l, r] [l,r] 的连续段(相邻的数构成连续段,比如1、2、3;2、3、4)的数目。题目强制在线。
- 思路:在区间 [ l , r ] [l, r] [l,r] 中,对于区间内任意一个数 x x x 来说,如果它可以作为连续段的起始位,那么它的前驱 x − 1 x - 1 x−1 的位置一定在区间 [ l , r ] [l, r] [l,r] 外。所以我们只需要统计,区间内所有元素中前驱的位置在区间 [ l , r ] [l, r] [l,r] 外的元素个数,即是连续段的个数。
那么反过来,我们也可以求出前驱位置在区间 [ l , r ] [l, r] [l,r] 内的元素个数,区间长度减去即可。所以我们应该怎么求区间中元素前驱位置在 [ l , r ] [l, r] [l,r] 中的元素个数呢?
可以考虑主席树求某区间值的个数,还可以分块块内排序暴力查找某区间值的个数。分块复杂度算起来是超了的,但是因为强制在线,异或区间导致区间范围变小,所以卡过了。
卡了cout!!!
主席树Code:
#include <bits/stdc++.h>
#define MID (l + r) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
const int maxN = 100005;
int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -f; ch = getchar(); }
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m, a[maxN], id[maxN], pioneer[maxN];
int lastans = 0;
int root[maxN], tot;
struct node{
int ls, rs, num;
}tree[maxN * 20];
void update(int &now, int pre, int l, int r, int k) {
now = ++ tot;
tree[now] = tree[pre];
tree[now].num ++ ;
if(l == r) return ;
int mid = MID;
if(mid >= k) update(tree[now].ls, tree[pre].ls, l, mid, k);
else update(tree[now].rs, tree[pre].rs, mid + 1, r, k);
}
int query(int i, int j, int l, int r, int ql, int qr) {
//区间[i + 1, j]中有多少值在[ql, qr]之间的数
if(ql <= l && qr >= r) return tree[j].num - tree[i].num;
int mid = MID;
if(qr <= mid) return query(tree[i].ls, tree[j].ls, l, mid, ql, qr);
else if(ql > mid) return query(tree[i].rs, tree[j].rs, mid + 1, r, ql, qr);
else return query(tree[i].ls, tree[j].ls, l, mid, ql, qr) + query(tree[i].rs, tree[j].rs, mid + 1, r, ql, qr);
}
map<pair<int, int>, int>mp;
int main() {
n = read();
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
id[a[i]] = i;
}
for(int i = 1; i <= n; ++ i ) {
pioneer[i] = id[a[i] - 1];
}
int UP = n;
for(int i = 1; i <= n; ++ i ) {
update(root[i], root[i - 1], 0, UP, pioneer[i]);
}
m = read();
while(m -- ) {
int l, r; l = read(), r = read();
l = l ^ lastans, r = r ^ lastans;
if(l < 1 || r > n || l > r) continue;
if(mp.find(make_pair(l, r)) != mp.end()) {
lastans = mp[make_pair(l, r)];
} else {
lastans = r - l + 1 - query(root[l - 1], root[r], 0, UP, l, r);
mp[make_pair(l, r)] = lastans;
}
printf("%d\n", lastans);
// cout << lastans << endl;
}
return 0;
}
分块Code:
#include <bits/stdc++.h>
using namespace std;
const int maxN = 100005;
int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -f; ch = getchar(); }
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m;
int a[maxN], id[maxN];
int pre[maxN];
int lastans = 0;
int block[maxN], blockNum;
vector<int>vt[maxN];
void Force(int l, int r, int L, int R, int &ans) {
for(int i = l; i <= r; ++ i ) {
if(pre[i] > R || pre[i] < L) ++ ans;
}
}
int query(int l, int r) {
int ans = 0;
if(block[l] == block[r]) {
Force(l, r, l, r, ans);
} else {
Force(l, block[l] * blockNum, l, r, ans);
Force((block[r] - 1) * blockNum + 1, r, l, r, ans);
for(int i = block[l] + 1; i <= block[r] - 1; ++ i ) {
ans += lower_bound(vt[i].begin(), vt[i].end(), l) - vt[i].begin();
ans += (blockNum - (upper_bound(vt[i].begin(), vt[i].end(), r) - vt[i].begin()));
}
}
return ans;
}
int main() {
n = read();
blockNum = sqrt(n);
for(int i = 1; i <= n; ++ i ) {
a[i] = read();
id[a[i]] = i;
}
for(int i = 1; i <= n; ++ i ) {
pre[i] = id[a[i] - 1];
block[i] = (i - 1) / blockNum + 1;
vt[block[i]].emplace_back(pre[i]);
}
for(int i = 1; i <= block[n]; ++ i ) {
sort(vt[i].begin(), vt[i].end());
}
m = read();
while(m -- ) {
int l, r;
l = read() ^ lastans, r = read() ^ lastans;
if(l < 1 || r > n || l > r) continue;
lastans = query(l, r);
// cout << lastans << endl;
printf("%d\n", lastans);
}
return 0;
}