二维莫队
二维莫队,顾名思义就是每个状态有四个方向可以扩展。
二维莫队每次移动指针要操作一行或者一列的数,具体实现方式与普通的一维莫队类似,这里不再赘述。这里重点讲块长选定部分。
块长选定
记询问次数为
那么指针
所以只需令
注意这样计算
最终,计算部分时间复杂度是
例题 1
BZOJ 2639 矩形计算
输入一个
数据范围:
解题思路
先离散化,二维莫队时用一个数组记录每个数当前出现的次数即可。
示例代码
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
int n, m, q, a[201][201];
long long ans[100001];
int disc[250001], cntdisc; // 离散化用
int blocklen, counts[40001];
long long now;
struct Question {
int x1, y1, x2, y2, qid;
bool operator<(Question tmp) const {
if (x1 / blocklen != tmp.x1 / blocklen) return x1 < tmp.x1;
if (y1 / blocklen != tmp.y1 / blocklen) return y1 < tmp.y1;
if (x2 / blocklen != tmp.x2 / blocklen) return x2 < tmp.x2;
return y2 < tmp.y2;
}
} Q[100001];
int Qcnt;
void mo_algo_row(int id, int val, int Y1, int Y2) {
for (int i = Y1; i <= Y2; i++)
now -= (long long)counts[a[id][i]] * counts[a[id][i]],
counts[a[id][i]] += val,
now += (long long)counts[a[id][i]] * counts[a[id][i]];
}
void mo_algo_column(int id, int val, int X1, int X2) {
for (int i = X1; i <= X2; i++)
now -= (long long)counts[a[i][id]] * counts[a[i][id]],
counts[a[i][id]] += val,
now += (long long)counts[a[i][id]] * counts[a[i][id]];
}
void mo_algo() {
blocklen = pow(n * m, 0.5) / pow(q, 0.25);
if (blocklen < 1) blocklen = 1;
sort(Q + 1, Q + 1 + Qcnt);
int X1 = 1, Y1 = 1, X2 = 0, Y2 = 0;
for (int i = 1; i <= Qcnt; i++) {
while (X1 > Q[i].x1) mo_algo_row(--X1, 1, Y1, Y2);
while (X2 < Q[i].x2) mo_algo_row(++X2, 1, Y1, Y2);
while (Y1 > Q[i].y1) mo_algo_column(--Y1, 1, X1, X2);
while (Y2 < Q[i].y2) mo_algo_column(++Y2, 1, X1, X2);
while (X1 < Q[i].x1) mo_algo_row(X1++, -1, Y1, Y2);
while (X2 > Q[i].x2) mo_algo_row(X2--, -1, Y1, Y2);
while (Y1 < Q[i].y1) mo_algo_column(Y1++, -1, X1, X2);
while (Y2 > Q[i].y2) mo_algo_column(Y2--, -1, X1, X2);
ans[Q[i].qid] = now;
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) cin >> a[i][j], disc[++cntdisc] = a[i][j];
sort(disc + 1, disc + 1 + cntdisc);
cntdisc = unique(disc + 1, disc + cntdisc + 1) - disc - 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = lower_bound(disc + 1, disc + 1 + cntdisc, a[i][j]) - disc;
cin >> q;
for (int i = 1; i <= q; i++) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
if (x1 > x2) swap(x1, x2);
if (y1 > y2) swap(y1, y2);
Q[++Qcnt] = {x1, y1, x2, y2, i};
}
mo_algo();
for (int i = 1; i <= q; ++i) cout << ans[i] << '\n';
return 0;
}
例题 2
首先和上一题一样,需要离散化整个矩阵。但是需要注意,本题除了需要对数值进行分块,还需要对数值的值域进行分块,才能求出答案。
这里还需要用到奇偶化排序进行优化,具体内容请见 普通莫队算法。
对于本题而言,时间限制不那么宽,注意代码常数的处理。取的块长计算值普遍较小,
示例代码
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int n, q, a[501][501], ans[60001];
int disc[250001], cntdisc; // 离散化用
int nn;
int blockId[501], blocklen; // 分块
int rangeblockId[250001], rangeblocklen; // 值域分块
int counts[250001], countsum[501]; // 该值次数及值域块总和
struct Position {
int x, y;
};
vector<Position> pos[250001];
struct Question {
int x1, y1, x2, y2, k, qid;
bool operator<(Question tmp) const {
if (blockId[x1] != blockId[tmp.x1]) return blockId[x1] < blockId[tmp.x1];
if (blockId[y1] != blockId[tmp.y1])
return blockId[x1] & 1 ? y1 < tmp.y1 : y1 > tmp.y1;
if (blockId[y2] != blockId[tmp.y2])
return blockId[y1] & 1 ? y2 < tmp.y2 : y2 > tmp.y2;
else
return blockId[y2] & 1 ? x2 < tmp.x2 : x2 > tmp.x2;
}
} Q[60001];
int Qcnt;
void mo_algo() {
blocklen = 11;
for (int i = 1; i <= n; ++i) blockId[i] = (i - 1) / blocklen + 1;
rangeblocklen = n + 1;
for (int i = 1; i <= nn; ++i) rangeblockId[i] = (i - 1) / rangeblocklen + 1;
counts[a[1][1]] = countsum[rangeblockId[a[1][1]]] = 1;
sort(Q + 1, Q + 1 + Qcnt);
int L = 1, R = 1, D = 1, U = 1;
for (int i = 1; i <= q; ++i) {
while (R < Q[i].y2) {
++R;
for (int i = U; i <= D; ++i)
++counts[a[i][R]], ++countsum[rangeblockId[a[i][R]]];
}
while (L > Q[i].y1) {
--L;
for (int i = U; i <= D; ++i)
++counts[a[i][L]], ++countsum[rangeblockId[a[i][L]]];
}
while (D < Q[i].x2) {
++D;
for (int i = L; i <= R; ++i)
++counts[a[D][i]], ++countsum[rangeblockId[a[D][i]]];
}
while (U > Q[i].x1) {
--U;
for (int i = L; i <= R; ++i)
++counts[a[U][i]], ++countsum[rangeblockId[a[U][i]]];
}
while (R > Q[i].y2) {
for (int i = U; i <= D; ++i)
--counts[a[i][R]], --countsum[rangeblockId[a[i][R]]];
--R;
}
while (L < Q[i].y1) {
for (int i = U; i <= D; ++i)
--counts[a[i][L]], --countsum[rangeblockId[a[i][L]]];
++L;
}
while (D > Q[i].x2) {
for (int i = L; i <= R; ++i)
--counts[a[D][i]], --countsum[rangeblockId[a[D][i]]];
--D;
}
while (U < Q[i].x1) {
for (int i = L; i <= R; ++i)
--counts[a[U][i]], --countsum[rangeblockId[a[U][i]]];
++U;
}
int res = 1, cnt = 0;
while (cnt + countsum[res] < Q[i].k && res <= rangeblockId[nn])
cnt += countsum[res], ++res;
res = (res - 1) * rangeblocklen + 1;
while (cnt + counts[res] < Q[i].k && res <= nn) cnt += counts[res], ++res;
ans[Q[i].qid] = disc[res];
}
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q;
nn = n * n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
int x;
cin >> x;
a[i][j] = disc[++cntdisc] = x;
}
sort(disc + 1, disc + 1 + cntdisc);
cntdisc = unique(disc + 1, disc + cntdisc + 1) - disc - 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
a[i][j] = lower_bound(disc + 1, disc + 1 + cntdisc, a[i][j]) - disc;
for (int i = 1; i <= q; ++i) {
int x1, y1, x2, y2, k;
cin >> x1 >> y1 >> x2 >> y2 >> k;
Q[++Qcnt] = {x1, y1, x2, y2, k, i};
}
mo_algo();
for (int i = 1; i <= q; ++i) cout << ans[i] << '\n';
return 0;
}
最后更新: 2023年9月24日
创建日期: 2023年5月17日
创建日期: 2023年5月17日