P3755 题解

前言

题目传送门!

更好的阅读体验?

为啥要用分块呀,cdq 分治好写,而且跑得比分块快!

前置知识:三维偏序模版。

思路

\(s_{i, j}\) 表示:对角坐标为 \((-\infty, -\infty)\)\((i, j)\) 的矩形内的点权之和。

那么类似二维前缀和:对角坐标为 \((x1,y1)\)\((x2, y2)\) 的矩形内的点权之和,可以表示成 \(s_{x2,y2} -s_{x1-1,y2} - s_{x2,y1-1} + s_{x1-1,y1-1}\)

这样,我们发现,每个询问可以拆成四个点,用这四个点的答案即可求出询问。我们称这个拆出来的点叫查询点

考虑转化为偏序问题(这样能用 cdq 求解)。

对于一个属性,记录 \(a,b\) 表示 \(s_{i,j}\) 中的 \(i\)\(j\)。为了方便,再记录 \(c\) 表示:当前属性是普通点还是查询点。

那么,若属性 \(X\) 可以计算 \(Y\) 的贡献入内,需要满足:

\(\begin{cases}Y_a \le X_a\\Y_b \le X_b\\Y_c < X_c\end{cases}\)

前面两个限制表示下标要包含在矩形内才行。第三个限制表示 \(X\) 是查询点 且 \(Y\) 是普通点。

然后就是一个三维偏序板子了,用板子的 \(O(n \log^2 n)\) 求解也能通过。但是我们可以做到 \(O(n \log n)\)

这里有一个细节,\(c\) 显然只能是 \(0\)\(1\)

那么我们不必使用树状数组了,使用一个变量记录即可。

具体参考代码。

完整代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 5e5 + 5;
struct Data {
	int a, b, c, k; //c: 1代表点,0代表查询的点 
	LL sum;
	int id;
} x[N], t[N];
bool operator == (Data p, Data q) {return p.a == q.a && p.b == q.b && p.c == q.c;}
bool cmp(Data p, Data q)
{
	if (p.a != q.a) return p.a < q.a;
	if (p.b != q.b) return p.b < q.b;
	return p.c < q.c;
}
void MergeSort(int l, int r) //三维偏序板子
{
	if (l >= r) return;
	int mid = (l + r) >> 1;
	MergeSort(l, mid), MergeSort(mid + 1, r);
	int i = l, j = mid + 1, cur = 0;
	LL sum = 0; //由于c只有0/1,所以用变量即可,省略树状数组
	while (i <= mid && j <= r)
		if (x[i].b <= x[j].b) sum += !x[i].c * x[i].k, t[++cur] = x[i], i++;
		else x[j].sum += sum, t[++cur] = x[j], j++;
	while (i <= mid) sum += x[i].c * x[i].k, t[++cur] = x[i], i++;
	while (j <= r) x[j].sum += sum, t[++cur] = x[j], j++;
	for (i = l, j = 1; j <= cur; i++, j++) x[i] = t[j];
}
LL ans[N];
int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) //输入部分就是按照上文的建模来的
	{
		scanf("%d%d%d", &x[i].a, &x[i].b, &x[i].k);
		x[i].c = x[i].sum = 0;
		x[i].id = i;
	}
	int cur = n;
	for (int i = 1; i <= m; i++)
	{
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		x[++cur] = (Data){x2, y2, 1, 1}, x[cur].id = i; //拆成四个点
		x[++cur] = (Data){x1 - 1, y2, 1, -1}, x[cur].id = i;
		x[++cur] = (Data){x2, y1 - 1, 1, -1}, x[cur].id = i;
		x[++cur] = (Data){x1 - 1, y1 - 1, 1, 1}, x[cur].id = i;
	}
	sort(x + 1, x + cur + 1, cmp); //由于c肯定不同,所以不用去重 
	MergeSort(1, cur);
	for (int i = 1; i <= cur; i++)
		if (x[i].c)
			ans[x[i].id] += x[i].sum * x[i].k;
	for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	return 0;
}

希望能帮助到大家!

posted @ 2022-10-17 12:55  liangbowen  阅读(47)  评论(0编辑  收藏  举报