• 合辑:CDQ分治


    听考拉的主人说CDQ分治是她考NOI的时候现yy出来的,太强了Orz

    CDQ分治主要是一种思想,用来处理离线的问题。

    学长说可以优化DP,但我觉得适用面还蛮窄的,主要还是处理序列问题,最主要的应该就是偏序。

    CDQ分治的主要思想就是用前一个子问题解决后一个子问题,比如你把一个询问区间二分,左边的区间维护答案,右边的区间查询左边区间维护的答案。至于右边的维护操作和左边的操作?交给下一层去解决了。

    这就限制了CDQ分治的适用范围,需要修改操作对询问的贡献是独立的且互不影响。

    Problem A:陌上花开

    维护三维偏序。

    树套树裸题,切了,走了(误

    树套树又臭又长能不写就不写,CDQ好写好调,来写CDQ

    先想二维偏序我们是怎么解决的:先按照第一维排个序,在第二维统计答案

    三维偏序多了一维,还是在最外层第三维统计答案,第二维套个CDQ。

    具体一点,先按a排个序,顺便去重。然后跑CDQ,通过跑归并把b排序,同时用数据结构维护c的偏序,这里是树状数组,同时之前的归并保证了b的有序。

    #include <bits/stdc++.h>
    
    const int N = 200000 + 233;
    int n, k, ans[N];
    struct Flower {int a, b, c, cnt, f;} a[N], b[N];
    
    bool cmp(const Flower &x, const Flower &y) {
    	return (x.a == y.a && x.b == y.b) ? x.c < y.c : (x.a == y.a) ? x.b < y.b : x.a < y.a;
    }
    
    struct BIT {
    	int t[N];
    	BIT() {memset(t, 0, sizeof(t));}
    	inline void Add(int x, int v) {
    		while (x <= k) t[x] += v, x += x & -x;
    	}
    	inline int Ask(int x) {
    		int ret = 0;
    		while (x) ret += t[x], x -= x & -x;
    		return ret;
    	}
    } bit;
    
    void CDQ(int l, int r) {
    	if (l == r) return;
    	int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
    	CDQ(l, mid), CDQ(mid + 1, r);
    	while (pl <= mid || pr <= r) {
    		if ((a[pl].b <= a[pr].b && pl <= mid) || (pr > r)) bit.Add(a[pl].c, a[pl].cnt), b[p++] = a[pl++];
    		else a[pr].f += bit.Ask(a[pr].c), b[p++] = a[pr++];
    	}
    	for (int i = l; i <= mid; i++) bit.Add(a[i].c, -a[i].cnt);
    	for (int i = l; i <= r; i++) a[i] = b[i];
    }
    
    signed main() {
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n; i++)
    		scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].c), a[i].cnt = 1;
    	std::sort(a + 1, a + 1 + n, cmp);
    	int p = 1;
    	for (int i = 2; i <= n; i++) {
    		if (a[i].a == a[p].a && a[i].b == a[p].b && a[i].c == a[p].c) a[p].cnt++;
    		else a[++p] = a[i];
    	}
    	CDQ(1, p);
    	for (int i = 1; i <= p; i++) ans[a[i].f + a[i].cnt - 1] += a[i].cnt;
    	for (int i = 0; i < n; i++) printf("%d
    ", ans[i]);
    	return 0;
    }
    

    Problem B & C: Mokia & 简单题

    双倍的快乐

    查询矩阵权值容易想到二维前缀和,把右上角和左下角的加上,把左上角和右下角的减掉。查询操作就被拆成了4个。

    关于修改操作,一个修改对查询有贡献,当且仅当它的xyt都不大于查询,这就又成了三维偏序,继续CDQ。

    老生长谈的问题,y1之类的变量名是会撞的,你必CE。解决方法两个,一个是你换变量名,或者如果你像我一样固执,可以开个namespace规避打击。

    简单题在Luogu是强制在线的,双倍经验请谨慎,你CDQ必死(

    #include <bits/stdc++.h>
    
    namespace Gekoo {
    	const int N = 2000000 + 233;
    	int s, w, qwq, qaq, ans[N];
    
    	struct Command {
    		int opt, x, y, a, p;
    	} cmd[N], tmp[N];
    
    	struct Bit {
    		int t[N];
    		inline void Add(int p, int d) {
    			for (; p <= w; p += p & -p) t[p] += d;
    		}
    		inline int Ask(int p) {
    			int ret = 0;
    			for (; p; p -= p & -p) ret += t[p];
    			return ret;
    		} 
    	} BIT;
    
    	inline int R() {
    		int a = 0; char c = getchar();
    		while (!isdigit(c)) c = getchar();
    		while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    		return a;
    	}
    
    	void CDQ(int l, int r) {
    		if (l == r) return;
    		int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
    		CDQ(l, mid), CDQ(mid + 1, r);
    		while (pl <= mid || pr <= r) {
    			if ((cmd[pl].x <= cmd[pr].x && pl <= mid) || pr > r) {
    				if (cmd[pl].opt == 1) BIT.Add(cmd[pl].y, cmd[pl].a);
    				tmp[p++] = cmd[pl++];
    			} else {
    				if (cmd[pr].opt == 2) {
    					if (cmd[pr].p > 0) ans[cmd[pr].p] += BIT.Ask(cmd[pr].y);
    					else ans[-cmd[pr].p] -= BIT.Ask(cmd[pr].y);
    				}
    				tmp[p++] = cmd[pr++];
    			}
    		}
    		for (int i = l; i <= mid; i++) 
    			if (cmd[i].opt == 1) BIT.Add(cmd[i].y, -cmd[i].a);
    		for (int i = l; i <= r; i++) cmd[i] = tmp[i];
    	}
    
    	signed QAQ() {
    		s = R(), w = R();
    		while (19260817 < 20030325) {
    			int opt = R();
    			if (opt == 3) break;
    			if (opt == 1) cmd[++qwq].opt = 1, cmd[qwq].x = R(), cmd[qwq].y = R(), cmd[qwq].a = R();
    			if (opt == 2) {
    				int x1 = R(), y1 = R(), x2 = R(), y2 = R();
    				ans[++qaq] = (x2 - x1 + 1) * (y2 - y1 + 1) * s;
    				cmd[++qwq].opt = 2, cmd[qwq].p = qaq, cmd[qwq].x = x1 - 1, cmd[qwq].y = y1 - 1;
    				cmd[++qwq].opt = 2, cmd[qwq].p = qaq, cmd[qwq].x = x2, cmd[qwq].y = y2;
    				cmd[++qwq].opt = 2, cmd[qwq].p = -qaq, cmd[qwq].x = x1 - 1, cmd[qwq].y = y2;
    				cmd[++qwq].opt = 2, cmd[qwq].p = -qaq, cmd[qwq].x = x2, cmd[qwq].y = y1 - 1;
    			}
    		}
    		CDQ(1, qwq);
    		for (int i = 1; i <= qaq; i++)
    			printf("%d
    ", ans[i]);
    		return 0;
    	}
    }
    
    signed main() {
    	return Gekoo::QAQ();
    }
    

    Problem C: 天使玩偶

    绝对值维护不能,考虑通过坐标的整体移动把坐标全变成正的,然后每个点只与在他左上方的点算距离。式子就变成 (dis = (x_1 + y_1) - (x_2 + y_2)).

    为了让每个点都可以被算到,把所有点按90度转四次,这样每个点都有机会到计算点的左上角。

    注意细节:树状数组和统计数组记得赋初值!不然必死。。

    还有,我写的时候sort写在统计后头了,调半天调不出来。。。这是错误的,我们要保证统计的时候点按时间排好序,否则对不上,会错位。

    这题在Luogu有巨卡常数的数据,这个算法显然是可以继续优化的,但我选择了吸氧(逃

    #include <bits/stdc++.h>
    
    const int N = 1000000 + 233, INF = 0x3f3f3f3f;
    int n, m, ans[N], real_ans[N], mx_x, mx_y;
    struct Doll {
    	int x, y, t, id;
    	friend bool operator <(Doll x, Doll y) {
    		return x.id < y.id;
    	}
    } d[N], tmp[N];
    
    struct Bit {
    	int t[N];
    	inline void Add(int x, int d) {
    		for (; x <= mx_y; x += x & -x) t[x] = std::max(t[x], d);
    	}
    	inline int Ask(int x) {
    		int ret = -INF;
    		for (; x; x -= x & -x) ret = std::max(ret, t[x]);
    		return ret;
    	}
    	inline void Clear(int x) {
    		for (; x <= mx_y; x += x & -x) t[x] = -INF;
    	}
    } BIT;
    
    inline int R() {
    	int a = 0; char c = getchar();
    	while (!isdigit(c)) c = getchar();
    	while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    	return a;
    }
    
    void CDQ(int l, int r) {
    	if (l == r) return;
    	int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
    	CDQ(l, mid), CDQ(mid + 1, r);
    	while (pl <= mid || pr <= r) {
    		if ((pl <= mid && d[pl].x <= d[pr].x) || pr > r) {
    			if (d[pl].t == 1) BIT.Add(d[pl].y, d[pl].x + d[pl].y);
    			tmp[p++] = d[pl++];
    		}
    		else {
    			if (d[pr].t == 2) ans[d[pr].id] = std::max(ans[d[pr].id], BIT.Ask(d[pr].y));
    			tmp[p++] = d[pr++];
    		}
    	}
    	for (int i = l; i <= mid; i++) BIT.Clear(d[i].y);
    	for (int i = l; i <= r; i++) d[i] = tmp[i];
    }
    
    signed main() {
    	n = R(), m = R();
    	for (int i = 1; i <= n; i++)
    		d[i].x = R() + 1, d[i].y = R() + 1, d[i].t = 1, d[i].id = i, mx_x = std::max(d[i].x, mx_x), mx_y = std::max(d[i].y, mx_y);
    	for (int i = 1; i <= m; i++)
    		d[i + n].t = R(), d[i + n].id = i + n, mx_x = std::max(d[i + n].x = R() + 1, mx_x), mx_y = std::max(d[i + n].y = R() + 1, mx_y);
    	for (int i = 1; i <= n + m; i++)
    		real_ans[i] = INF, ans[i] = -INF;
    	for (int i = 0; i <= mx_x + mx_y + 2; i++) BIT.t[i] = -INF;
    	mx_x++, mx_y++, CDQ(1, n + m);
    
    	std::sort(d + 1, d + 1 + n + m);
    	for (int i = 1; i <= n + m; i++)
    		real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].x = mx_x - d[i].x, ans[i] = -INF;
    	CDQ(1, n + m);
    
    	std::sort(d + 1, d + 1 + n + m);
    	for (int i = 1; i <= n + m; i++)
    		real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].y = mx_y - d[i].y, ans[i] = -INF;
    	CDQ(1, n + m);
    
    	std::sort(d + 1, d + 1 + n + m);
    	for (int i = 1; i <= n + m; i++)
    		real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].x = mx_x - d[i].x, ans[i] = -INF; 
    	CDQ(1, n + m);
    
    	std::sort(d + 1, d + 1 + n + m);
    	for (int i = 1; i <= n + m; i++)
    		real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]);
    	for (int i = 1; i <= n + m; i++)
    		if (d[i].t == 2)
    			printf("%d
    ", real_ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    C# 单点登录
    长度12的数组,要求对数据分为3组,每组数据对应位置的数字为前几位的和,并返回12位数组
    react项目初始化
    vue 过滤器的使用(解决forEach遇到的问题)
    nuxt中less使用
    vue项目less 使用
    Webpack中的sourceMap配置
    webpack 同一文件打包两次生成两个文件
    webpack---图片打包前和打包后名称一致的配置
    The computed property "userName" is already defined in data.
  • 原文地址:https://www.cnblogs.com/gekoo/p/11249445.html
Copyright © 2020-2023  润新知