bzoj5016 [SNOI2017]一个简单的询问
给你一个长度为 \(N\) 的序列 \(a_i\) ,\(1\leq i\leq N\) ,和 \(Q\) 组询问,每组询问读入 \(l_1,\ r_1,\ l_2,\ r_2\) ,需输出
\[\sum\limits_{x=0}^\infty \text{get}(l_1,\ r_1,\ x)\cdot \text{get}(l_2,\ r_2,\ x) \]\(\text{get}(l,\ r,\ x)\) 表示计算区间 \([l,\ r]\) 中,数字 \(x\) 出现了多少次。
\(1\leq N,\ Q\leq5\times10^4,\ a_i\in[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\)
\(\therefore\) $$(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$$
\(\therefore\) $$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\n", ans[i]);
}
return 0;
}