• [AHOI2013]作业


    Problem

    给定一个长度为 (n) 的序列 (A),每次给定四个数 (l,r,a,b),查询 ([l,r]) 内值域在 ([a,b]) 内:

    1、数的个数;

    2、不同的数的个数。

    Solution

    问题 2 可以转化成三维偏序。这题用来练手基础数据结构。

    扫描线+树套树

    时间复杂度 (mathcal O(nlog^2n)),空间复杂度 (O(nlog n))(树状数组+平衡树)或者 (mathcal O(nlog^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
    ", ansx[i], ansy[i]);
    	return 0;
    }
    

    CDQ+树状数组

    时间复杂度 (mathcal O(nlog^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
    ", ansx[i], ansy[i]);
    	return 0;
    }
    

    莫队+分块

    对询问莫队,对值域分块。

    这里注意:

    莫队的复杂度(O(nsqrt m))

    分块的复杂度(O(msqrt n))

    两者复杂度并不相同

    总时间复杂度 (mathcal O(nsqrt m+msqrt 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
    ", 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
    ", query(1, n));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Mysql case when 根据数字排序 返回 string类型问题
    .NET Core 面试题
    一个人如何完成一整个网站的开发(推荐好文,看完绝对让你回味无穷)转载
    (转)实现C#中等价于的Javascript中的Math.Random()的函数,以得到一个随机数,double类型的,大于0小于1的,17位精度的
    brew安装指定版本的软件
    服务器被植入挖矿木马的心酸过程
    我把双系统的win10抹除了现在开机只按option还是会出现双系统选择,怎么把那个win10给取消了或删除掉
    java.lang.Exception: The server rejected the connection: None of the protocols were accepted
    Linux 技巧:让进程在后台可靠运行的几种方法
    jenkins backup and migration
  • 原文地址:https://www.cnblogs.com/ac-evil/p/14266886.html
Copyright © 2020-2023  润新知