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;
}
希望能帮助到大家!