给你一个长度为 (N) 的序列 (a_i) ,(1leq ileq N) ,和 (Q) 组询问,每组询问读入 (l_1, r_1, l_2, r_2) ,需输出
[sumlimits_{x=0}^infty ext{get}(l_1, r_1, x)cdot ext{get}(l_2, r_2, x) ]( ext{get}(l, r, x)) 表示计算区间 ([l, r]) 中,数字 (x) 出现了多少次。
(1leq N, Qleq5 imes10^4, a_iin[1, N])
莫队
如果直接暴力莫队,时间复杂度是假的(
首先,如果 (l_1=l_2=1) ,是可以直接用莫队 (O(sqrt n)) 求出的
现在把询问拆成 ([1, x][1, y]) 的形式
令 (A_1=[l_1, r_1], A_2=[l_2, r_2], B_1=[1, l_1-1], B_2=[1, l_2-1])
先看看 ((A_1+B_1)(A_2+B_2)) 是什么
(A_1A_2+A_1B_2+A_2B_1+B_1B_2)
(A_1A_2, B_1B_2) 是可以直接求的
再把 (A_1B_2) 拆成 ((A_1+B_1)B_2-B_1B_2) ,把 (A_2B_1) 拆成 ((A_2+B_2)B_1-B_1B_2)
( herefore) $$(A_1+B_1)(A_2+B_2)=A_1A_2+(A_1+B_1)B_2+(A_2+B_2)B_1-B_1B_2$$
( herefore) $$A_1A_2=(A_1+B_1)(A_2+B_2)-(A_1+B_1)B_2-(A_2+B_2)B_1+B_1B_2$$
容易发现,上式右侧每一项都是形如 ([1, x]) 的,然后就上莫队辣
时间复杂度 (O(sqrt n))
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 10;
ll ans[maxn];
int n, m, tot, bsz, a[maxn], bl[maxn], c1[maxn], c2[maxn];
struct Query {
int l, r, op, tid;
Query(int _l = 0, int _r = 0, int _o = 0, int _t = 0) :
l(_l), r(_r), op(_o), tid(_t) {}
bool operator < (const Query& o) const {
return bl[l] == bl[o.l] ? r > o.r : l < o.l;
}
} Q[maxn << 2];
int main() {
scanf("%d", &n);
bsz = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
bl[i] = (i - 1) / bsz + 1;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
int l1, l2, r1, r2;
scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
Q[++tot] = Query(r1, r2, 1, i);
if (l1 > 1) Q[++tot] = Query(l1 - 1, r2, -1, i);
if (l2 > 1) Q[++tot] = Query(r1, l2 - 1, -1, i);
if (l1 > 1 && l2 > 1) Q[++tot] = Query(l1 - 1, l2 - 1, 1, i);
}
sort(Q + 1, Q + tot + 1);
int l = 0, r = 0; ll now = 0;
for (int i = 1; i <= tot; i++) {
while (l < Q[i].l) {
l++, c1[a[l]]++, now += c2[a[l]];
}
while (l > Q[i].l) {
c1[a[l]]--, now -= c2[a[l]], l--;
}
while (r < Q[i].r) {
r++, c2[a[r]]++, now += c1[a[r]];
}
while (r > Q[i].r) {
c2[a[r]]--, now -= c1[a[r]], r--;
}
ans[Q[i].tid] += Q[i].op * now;
}
for (int i = 1; i <= m; i++) {
printf("%lld
", ans[i]);
}
return 0;
}