距离上次写莫队已经过去半年了,莫队是什么东西都快忘了,而今天杭电多校的J题考了莫队的运用。所以,裂开。
回到正文。莫队在我看来是是一个通过分块和排序,使得在离线询问的前提下,能过减少很多不必要运算的过程。
分块的大小一般来说是,这题是数据范围是1e5,所以 k 取300左右。
聊回此题,我们要先对横坐标分块排序,这样就可以使得询问的区间在一个小范围内波动。
struct AC
{
int x1, y1, x2, y2, idx;
bool friend operator<(AC m1, AC m2) {
if (m1.x1 / k != m2.x1 / k)return m1.x1 / k < m2.x1 / k;
else {
if((m1.x1/k)&1)return m1.x2 < m2.x2 ;
else return m1.x2 > m2.x2;
}
}
}p[MAXN];
这里注意一个小细节,如果当前分块是奇数块,就正着排,否则就反正排,这种玄学优化可以加速100ms左右。
计算答案的方式是对纵坐标分块
function<void(int)>add = [&](int t) {
v[t]++;
if (v[t] == 1)sum[t / k]++;
};
function<void(int)>dec = [&](int t) {
v[t]--;
if (v[t] == 0)sum[t / k]--;
};
sum数组的使用是一种类似前缀和的思想。
function<int(int)>cal = [&](int t) {
int ans = 0;
for (int i = 0; i < t / k; i++) {
ans += sum[i];
}
for (int i = (t / k)* k; i <= t; i++) {
ans += (v[i] > 0);
}
return ans;
};
这里要足以,至于为什么不是等于,可以手动画一画,这个点很难发现,我一开始是取等的,所以一致WA。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
#define k 303
struct AC
{
int x1, y1, x2, y2, idx;
bool friend operator<(AC m1, AC m2) {
if (m1.x1 / k != m2.x1 / k)return m1.x1 / k < m2.x1 / k;
else {
if((m1.x1/k)&1)return m1.x2 < m2.x2 ;
else return m1.x2 > m2.x2;
}
}
}p[MAXN];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector<int>s(n + 1);
for (int i = 1; i <= n; i++) cin >> s[i];
vector<int>sum(n + 1), v(n + 1);
for (int i = 1; i <= m; i++) {
cin >> p[i].x1 >> p[i].y1 >> p[i].x2 >> p[i].y2;
p[i].idx = i;
}
sort(p + 1, p + 1 + m);
int l = p[1].x1, r = p[1].x2;
function<void(int)>add = [&](int t) {
v[t]++;
if (v[t] == 1)sum[t / k]++;
};
function<void(int)>dec = [&](int t) {
v[t]--;
if (v[t] == 0)sum[t / k]--;
};
for (int i = l; i <= r; i++) {
add(s[i]);
}
vector<int>ans(m + 1);
function<int(int)>cal = [&](int t) {
int ans = 0;
for (int i = 0; i < t / k; i++) {
ans += sum[i];
}
for (int i = (t / k)* k; i <= t; i++) {
ans += (v[i] > 0);
}
return ans;
};
ans[p[1].idx] = cal(p[1].y2) - cal(p[1].y1 - 1);
for (int i = 2; i <= m; i++) {
while (l > p[i].x1) {
l--;
add(s[l]);
}
while (r < p[i].x2) {
r++;
add(s[r]);
}
while (l < p[i].x1) {
dec(s[l]);
l++;
}
while (r > p[i].x2) {
dec(s[r]);
r--;
}
ans[p[i].idx] = cal(p[i].y2) - cal(p[i].y1 - 1);
}
for (int i = 1; i <= m; i++) {
cout << ans[i] << endl;
}
}
return 0;
}