题目地址:
https://www.luogu.com.cn/problem/P2709
题目描述:
小B有一个长为
n
n
n的整数序列
a
a
a,值域为
[
1
,
k
]
[1,k]
[1,k]。他一共有
m
m
m个询问,每个询问给定一个区间
[
l
,
r
]
[l,r]
[l,r],求:
∑
i
=
1
k
c
i
2
\sum\limits_{i=1}^k c_i^2
i=1∑kci2其中
c
i
c_i
ci表示数字
i
i
i在
[
l
,
r
]
[l,r]
[l,r]中的出现次数。小B请你帮助他回答询问。
输入格式:
第一行三个整数
n
,
m
,
k
n,m,k
n,m,k。
第二行
n
n
n个整数,表示小B的序列。
接下来的
m
m
m行,每行两个整数
l
,
r
l,r
l,r。
输出格式:
输出
m
m
m行,每行一个整数,对应一个询问的答案。
数据范围:
对于
100
%
100\%
100%的数据,
1
≤
n
,
m
,
k
≤
5
×
1
0
4
1\le n,m,k \le 5\times 10^4
1≤n,m,k≤5×104。
可以用莫队,询问之间可以平均 O ( n ) O(\sqrt n) O(n)转移。参考https://blog.csdn.net/qq_46105170/article/details/125827776。代码如下:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 5e4 + 10;
int n, m, k, len;
int a[N], bel[N], cnt[N];
long res[N], ans;
struct Query {
int id, l, r;
} q[N];
void add(int x) {
ans -= cnt[x] * cnt[x];
cnt[x]++;
ans += cnt[x] * cnt[x];
}
void del(int x) {
ans -= cnt[x] * cnt[x];
cnt[x]--;
ans += cnt[x] * cnt[x];
}
int main() {
scanf("%d%d%d", &n, &m, &k);
len = sqrt(n);
for (int i = 1; i <= n; i++) {
bel[i] = i / len;
scanf("%d", &a[i]);
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q, q + m, [&](Query& q1, Query& q2) {
int l1 = q1.l, l2 = q2.l;
if (bel[l1] != bel[l2]) return bel[l1] < bel[l2];
int r1 = q1.r, r2 = q2.r;
if (bel[l1] & 1) return r1 < r2;
else return r1 > r2;
});
for (int k = 0, i = 0, j = 1; k < m; k++) {
int id = q[k].id, l = q[k].l, r = q[k].r;
while (i < r) add(a[++i]);
while (i > r) del(a[i--]);
while (j < l) del(a[j++]);
while (j > l) add(a[--j]);
res[id] = ans;
}
for (int i = 0; i < m; i++) printf("%ld\n", res[i]);
}
预处理时间复杂度 O ( m log m ) O(m\log m) O(mlogm),每次询问时间 O ( n ) O(\sqrt n) O(n),空间 O ( n + k ) O(n+k) O(n+k)。