>Link
luogu P2709
>Description
n
,
m
,
k
≤
5
∗
1
0
4
n,m,k\le 5*10^4
n,m,k≤5∗104
>解题思路
莫队——优美的暴力
考虑暴力的一种优化做法,我们记录上一次区间的左右端点,然后把左端点和右端点移到当前的位置,进行加数、减数操作维护左右端点内的每个数出现的次数
可是这样的时间复杂度仍然是
O
(
n
2
)
O(n^2)
O(n2) 的
我们现在把
n
n
n 分成
n
\sqrt n
n 块,每一块长度为
n
\sqrt n
n
对每个区间进行排序,左端点按照所属的块的序号排,然后在每个块内再按照右端点从小到大排
这样排完序后进行上面优化过的暴力,分析一下时间复杂度:
左端点:每次一个新的询问在同一个块内,所以移动的距离为
n
\sqrt n
n,一共有
m
m
m 个询问,总的为
O
(
m
n
)
O(m\sqrt n)
O(mn)
右端点:在同一个块内,所有询问移动的距离为
n
n
n,一共有
n
\sqrt n
n 个块,总的为
O
(
n
n
)
O(n\sqrt n)
O(nn)
所以时间复杂度为
O
(
n
n
)
O(n\sqrt n)
O(nn)
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 50010
#define LL long long
using namespace std;
struct node
{
int l, r, pos, id;
} q[N];
int n, m, k, a[N], st[N];
LL sum, tot[N], ans[N];
bool cmp (node aa, node bb)
{
if (aa.pos != bb.pos) return aa.pos < bb.pos;
return aa.r < bb.r;
}
int main()
{
scanf ("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);
int l, r, sq, pos;
sq = sqrt (n);
for (int i = 1; i <= sq; i++)
st[i] = i * sq;
for (int i = 1; i <= m; i++)
{
scanf ("%d%d", &l, &r);
pos = lower_bound (st + 1, st + 1 + sq, l) - st;
if (st[pos] < l) pos++;
q[i] = (node){l, r, pos, i};
}
sort (q + 1, q + 1 + m, cmp);
l = q[1].l, r = q[1].r;
for (int i = l; i <= r; i++)
{
sum += tot[a[i]] * (LL)2 + (LL)1;
tot[a[i]]++;
}
ans[q[1].id] = sum;
for (int i = 2; i <= m; i++)
{
while (r < q[i].r)
{
r++;
sum += tot[a[r]] * (LL)2 + (LL)1;
tot[a[r]]++;
}
while (r > q[i].r)
{
sum += -tot[a[r]] * (LL)2 + (LL)1;
tot[a[r]]--;
r--;
}
while (l < q[i].l)
{
sum += -tot[a[l]] * (LL)2 + (LL)1;
tot[a[l]]--;
l++;
}
while (l > q[i].l)
{
l--;
sum += tot[a[l]] * (LL)2 + (LL)1;
tot[a[l]]++;
}
ans[q[i].id] = sum;
}
for (int i = 1; i <= m; i++)
printf ("%lld\n", ans[i]);
return 0;
}