[AHOI2013]作业
Problem
给定一个长度为 \(n\) 的序列 \(A\),每次给定四个数 \(l,r,a,b\),查询 \([l,r]\) 内值域在 \([a,b]\) 内:
1、数的个数;
2、不同的数的个数。
Solution
问题 2 可以转化成三维偏序。这题用来练手基础数据结构。
扫描线+树套树
时间复杂度 \(\mathcal O(n\log^2n)\),空间复杂度 \(O(n\log n)\)(树状数组+平衡树)或者 \(\mathcal O(n\log^2n)\)(本代码写法,树状数组+主席树)
#include <bits/stdc++.h>
using std::sort;
const int N = 100005;
int n, m, a[N], pre[N], la[N], ansx[N], ansy[N];
struct Query { int p, q, x, y, id; } Q[N*2];
bool cmp(Query a, Query b) { return a.p < b.p; }
namespace SEG {
int lc[N*300], rc[N*300], sum[N*300], tot = 0;
void modify(int &o, int l, int r, int p) {
if (!o) o = ++tot;
sum[o]++;
if (l == r) return;
int mid = l+r>>1;
p <= mid ? modify(lc[o], l, mid, p) : modify(rc[o], mid+1, r, p);
}
int query(int o, int l, int r, int p) {
if (!o || l == r) return sum[o];
int mid = l+r>>1;
return p <= mid ? query(lc[o], l, mid, p) : sum[lc[o]] + query(rc[o], mid+1, r, p);
}
}
#define lowbit(x) (x & (-x))
int C[N];
void add(int i, int j) {
for (; i <= n; i += lowbit(i))
SEG::modify(C[i], 0, n-1, j);
}
int qry(int i, int j) {
int ans = 0;
for (; i; i -= lowbit(i))
ans += SEG::query(C[i], 0, n-1, j);
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), pre[i] = la[a[i]], la[a[i]] = i;
for (int i = 1; i <= m; i++) {
int l, r, x, y; scanf("%d%d%d%d", &l, &r, &x, &y);
Q[i*2-1] = (Query){l-1, l-1, x, y, -i};
Q[i*2] = (Query){r, l-1, x, y, i};
}
sort(Q+1, Q+m*2+1, cmp);
for (int i = 0, p = 1; i <= n; i++) {
if (i) add(a[i], pre[i]);
while (p <= m*2 && Q[p].p == i) {
if (Q[p].id > 0)
ansx[Q[p].id] += qry(Q[p].y, n-1) - qry(Q[p].x-1, n-1),
ansy[Q[p].id] += qry(Q[p].y, Q[p].q) - qry(Q[p].x-1, Q[p].q);
if (Q[p].id < 0)
ansx[-Q[p].id] -= qry(Q[p].y, n-1) - qry(Q[p].x-1, n-1),
ansy[-Q[p].id] -= qry(Q[p].y, Q[p].q) - qry(Q[p].x-1, Q[p].q);
p++;
}
}
for (int i = 1; i <= m; i++) printf("%d %d\n", ansx[i], ansy[i]);
return 0;
}
CDQ+树状数组
时间复杂度 \(\mathcal O(n\log^2n)\),空间复杂度 \(\mathcal O(n)\)。
#include <bits/stdc++.h>
using std::sort;
const int N = 100005;
int n, m, a[N], la[N], pre[N], ansx[N], ansy[N], tot = 0;
struct Query { int x, y, z, id; } Q[N*6];
bool cmp1(Query a, Query b) { return a.x != b.x ? a.x < b.x : a.y != b.y ? a.y < b.y : abs(a.id) < abs(b.id); }
bool cmp2(Query a, Query b) { return a.y < b.y; }
#define lowbit(x) (x & (-x))
int C[N];
void add(int i, int v) {
for (; i <= n; i += lowbit(i)) C[i] += v;
}
int qry(int i) {
int ans = 0;
for (; i; i -= lowbit(i)) ans += C[i];
return ans;
}
void solve(int l, int r) {
if (l == r) return;
int mid = l+r>>1;
solve(l, mid), solve(mid+1, r);
sort(Q+l, Q+mid+1, cmp2), sort(Q+mid+1, Q+r+1, cmp2);
int p = l;
for (int i = mid+1; i <= r; i++) {
while (p <= mid && Q[p].y <= Q[i].y) {
if (!Q[p].id) add(Q[p].z, 1);
p++;
}
if (Q[i].id > 0)
ansx[Q[i].id] += qry(n), ansy[Q[i].id] += qry(Q[i].z);
if (Q[i].id < 0)
ansx[-Q[i].id] -= qry(n), ansy[-Q[i].id] -= qry(Q[i].z);
}
for (int i = l; i < p; i++)
if (!Q[i].id) add(Q[i].z, -1);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), pre[i] = la[a[i]], la[a[i]] = i, Q[++tot] = (Query){i, a[i], pre[i]+1, 0};
for (int i = 1; i <= m; i++) {
int l, r, x, y; scanf("%d%d%d%d", &l, &r, &x, &y);
Q[++tot] = (Query){l-1, x-1, l, i};
Q[++tot] = (Query){l-1, y, l, -i};
Q[++tot] = (Query){r, x-1, l, -i};
Q[++tot] = (Query){r, y, l, i};
}
sort(Q+1, Q+tot+1, cmp1);
solve(1, tot);
for (int i = 1; i <= m; i++) printf("%d %d\n", ansx[i], ansy[i]);
return 0;
}
莫队+分块
对询问莫队,对值域分块。
这里注意:
莫队的复杂度为 \(O(n\sqrt m)\);
分块的复杂度为 \(O(m\sqrt n)\);
两者复杂度并不相同。
总时间复杂度 \(\mathcal O(n\sqrt m+m\sqrt n)\),空间复杂度 \(\mathcal O(n)\)。
#include <bits/stdc++.h>
using std::sort; using std::max; using std::min;
const int N = 100005, B = 350;
int n, m, a[N], ansx[N], ansy[N], t[N], sum1[N/B+5], sum2[N/B+5], bl[N], blv[N], V = 0;
struct Queue { int l, r, x, y, id; } Q[N];
bool cmp(Queue a, Queue b) {
return bl[a.l] ^ bl[b.l] ? bl[a.l] < bl[b.l] : a.r < b.r;//bl[a.l] & 1 ? a.r < b.r : a.r > b.r;
}
void ins(int i) {
if (!t[a[i]]) sum2[blv[a[i]]]++;
t[a[i]]++; sum1[blv[a[i]]]++;
}
void del(int i) {
t[a[i]]--; sum1[blv[a[i]]]--;
if (!t[a[i]]) sum2[blv[a[i]]]--;
}
int calc(int l, int r, int op) {
int ans = 0; r = min(r, V);
if (blv[l] == blv[r])
for (int i = l; i <= r; i++) ans += op ? (bool)t[i] : t[i];
else {
for (int i = blv[l]+1; i <= blv[r]-1; i++) ans += op ? sum2[i] : sum1[i];
for (int i = l; i < (blv[l]+1)*B; i++) ans += op ? (bool)t[i] : t[i];
for (int i = blv[r]*B; i <= r; i++) ans += op ? (bool)t[i] : t[i];
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), bl[i] = i/B, V = max(V, a[i]);
for (int i = 1; i <= V; i++) blv[i] = i/B;
for (int i = 1; i <= m; i++)
scanf("%d%d%d%d", &Q[i].l, &Q[i].r, &Q[i].x, &Q[i].y), Q[i].id = i;
sort(Q+1, Q+m+1, cmp);
int pl = 1, pr = 0;
for (int i = 1; i <= m; i++) {
while (pl > Q[i].l) ins(--pl);
while (pl < Q[i].l) del(pl++);
while (pr > Q[i].r) del(pr--);
while (pr < Q[i].r) ins(++pr);
ansx[Q[i].id] = calc(Q[i].x, Q[i].y, 0), ansy[Q[i].id] = calc(Q[i].x, Q[i].y, 1);
}
for (int i = 1; i <= m; i++) printf("%d %d\n", ansx[i], ansy[i]);
return 0;
}
K-D Tree
对于 \(k\) 维的情形,偏序查询的最坏复杂度为 \(\mathcal O(n^{1-\frac1k})\)。
故时间复杂度为 \(\mathcal O(mn^{\frac23})\),空间复杂度为 \(\mathcal O(n)\)。
KD-Tree 的好处就是能够灵活处理高维的数据情况,并且支持修改、删除、在线询问。
#include <bits/stdc++.h>
using std::max; using std::min; using std::nth_element;
const int N = 100005;
int n, m, la[N], ql, qr, qx, qy, qz;
struct node { int x, y, z; } a[N];
int lc[N], rc[N], U[N], D[N], L[N], R[N], F[N], B[N], sz[N];
void pushup(int o) {
L[o] = R[o] = a[o].x; D[o] = U[o] = a[o].y; F[o] = B[o] = a[o].z;
if (lc[o]) L[o] = min(L[o], L[lc[o]]), R[o] = max(R[o], R[lc[o]]), D[o] = min(D[o], D[lc[o]]), U[o] = max(U[o], U[lc[o]]), F[o] = min(F[o], F[lc[o]]), B[o] = max(B[o], B[lc[o]]);
if (rc[o]) L[o] = min(L[o], L[rc[o]]), R[o] = max(R[o], R[rc[o]]), D[o] = min(D[o], D[rc[o]]), U[o] = max(U[o], U[rc[o]]), F[o] = min(F[o], F[rc[o]]), B[o] = max(B[o], B[rc[o]]);
sz[o] = sz[lc[o]] + sz[rc[o]] + 1;
}
bool cmpx(node a, node b) { return a.x < b.x; }
bool cmpy(node a, node b) { return a.y < b.y; }
bool cmpz(node a, node b) { return a.z < b.z; }
double sqr(double x) { return x*x; }
int build(int l, int r) {
if (l > r) return 0;
int mid = l+r>>1;
double avx = 0, avy = 0, avz = 0, vax = 0, vay = 0, vaz = 0;
for (int i = l; i <= r; i++)
avx += a[i].x, avy += a[i].y, avz += a[i].z;
avx /= r-l+1, avy /= r-l+1, avz /= r-l+1;
for (int i = l; i <= r; i++)
vax += sqr(a[i].x - avx), vay += sqr(a[i].y - avy), vaz += sqr(a[i].z - avz);
double maxv = max(max(vax, vay), vaz);
if (maxv == vax) nth_element(a+l, a+mid, a+r+1, cmpx);
else if (maxv == vay) nth_element(a+l, a+mid, a+r+1, cmpy);
else nth_element(a+l, a+mid, a+r+1, cmpz);
lc[mid] = build(l, mid-1); rc[mid] = build(mid+1, r);
pushup(mid); return mid;
}
int query(int l, int r) {
int mid = l+r>>1;
if (l > r || L[mid] > qr || R[mid] < ql || D[mid] > qy || U[mid] < qx || F[mid] > qz) return 0;
if (ql <= L[mid] && R[mid] <= qr && qx <= D[mid] && U[mid] <= qy && B[mid] <= qz) return sz[mid];
return (int)(ql <= a[mid].x && a[mid].x <= qr && qx <= a[mid].y && a[mid].y <= qy && a[mid].z <= qz) + query(l, mid-1) + query(mid+1, r);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
a[i] = (node){i, x, la[x]};
la[x] = i;
}
build(1, n);
while (m--) {
scanf("%d%d%d%d", &ql, &qr, &qx, &qy);
qz = n; printf("%d ", query(1, n));
qz = ql-1; printf("%d\n", query(1, n));
}
return 0;
}