把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【题解】P3374 【模板】树状数组 1 - $CDQ$ - 二维偏序

P3374 【模板】树状数组 1

声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

题目描述

如题,已知一个数列,你需要进行下面两种操作:

将某一个数加上 \(x\)

求出某区间每一个数的和。

前言

感觉最近学东西真的太快了...挺多都是囫囵吞枣,如果没有深入理解的话,做题就很难想到。这也是为什么我平时学得挺开心,一旦做题就懵逼的一大原因吧。

\(\\\)


\(\\\)

\(Solution\)

树状数组方法就不说了

关于 \(CDQ\),其实就是一个按照时间分治的东西(当然了,在某些题目里可以把某些条件转化为时间)

例如对于这道题来说,每个操作都是按照时间表的顺序进行的,前面的修改可能会影响后面的查询,但前面的查询不可能被后面的修改影响

那么我们可以尝试着把所有操作分成两个子问题,解决完每个子问题后,我们只需统计左区间对右区间的影响即可

如何统计这个影响呢?

我们注意到一个问题,对于某个询问操作 \(a\),有一个修改操作 \(b\) 对它产生影响,那么 \(b\) 除了在时间表上的位置要比 \(a\) 靠前之外,\(b\) 所修改的点的位置也应该在 \(a\) 所要查询的区间内的

对于很多个区间,似乎不太好统计答案...于是我们可以把一个区间变为两个前缀和相减,把原来的一个询问操作 \(a\) 变为两个求前缀和的操作

那么可以把左右区间的所有操作排个序,按照位置枚举各个操作,若修改操作 \(b\) 对某个前缀和操作 \(a\) 产生了影响,那么 \(b\) 一定对 \(a\) 即后面的所有前缀和操作都产生了影响,就可以用一个 \(sum\) 来累加产生的影响即可(感觉有点糊,具体看代码)

快排的时间是 \(O(n\log{n})\) 的,则总复杂度会有两个 \(\log\) ,会被卡掉

但是我们可以发现,由于是递归实现,那么左区间和右区间一定都是有序的,就可以用归并排序,一边归并一边计算贡献

完结撒花✿✿ヽ(°▽°)ノ✿

\(\\\)


\(\\\)

\(Code\)

#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++ i)
#define ll long long
using namespace std;
ll read();
const int N = 2e6 + 5;
int n, m, cnt, tot;
ll ans[N];
struct order{
	ll pos, k, val;//pos 为位置,k 为操作类型,val 为修改的值 / 询问的编号
}p[N], tmp[N];
void CDQ(int l, int r)
{
	if(l == r) return;
	ll mid = (l + r) >> 1;
	CDQ(l, mid), CDQ(mid + 1, r);
	ll sum = 0, i = l, j = mid + 1, o = l - 1;//归并的两个指针
	while(i <= mid && j <= r)
		if(p[i].pos <= p[j].pos) 
		{
			if(p[i].k == 1) sum += p[i].val;//如果是左区间的,那么就累加影响
			tmp[++ o] = p[i ++];
		}
		else 
		{
			if(p[j].k == 2) ans[p[j].val] -= sum;//此时累加的影响一定会对此处前缀和产生影响,因为那些操作的时间和位置都在此处之前
			if(p[j].k == 3) ans[p[j].val] += sum;//同上
			tmp[++ o] = p[j ++];
		}
	while(i <= mid) tmp[++ o] = p[i ++];
	while(j <= r)
	{
		if(p[j].k == 2) ans[p[j].val] -= sum;
		if(p[j].k == 3) ans[p[j].val] += sum;
		tmp[++ o] = p[j ++];
	}
	F(i, l, o) p[i] = tmp[i];//最后整体归并完成
}
int main()
{
	n = read(), m = read(), tot = n;
	F(i, 1, n) p[i].val = read(), p[i].pos = i, p[i].k = 1;//直接把原数组也看成一次操作
	for(int i = 1, op; i <= m; ++ i)
	{
		op = read();
		if(op == 1)
			p[++ tot].k = 1, p[tot].pos = read(), p[tot].val = read();
		else
		{
			p[++ tot].k = 2, p[tot].pos = read() - 1, p[tot].val = ++ cnt;
			p[++ tot].k = 3, p[tot].pos = read(), p[tot].val = cnt;
		}
	}
	CDQ(1, tot);
	F(i, 1, cnt) printf("%lld\n", ans[i]);
	return 0;
}
ll read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
posted @ 2020-05-05 20:59  Bn_ff  阅读(186)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end