[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;
}
posted @ 2021-01-12 14:42  AC-Evil  阅读(78)  评论(0编辑  收藏  举报