0%

洛谷2709题:小B的询问(莫队裸题)

题目

题目链接

https://www.luogu.org/problemnew/show/P2709

题目描述

小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。

输入输出格式

输入格式:

第一行,三个整数N、M、K。

第二行,N个整数,表示小B的序列。

接下来的M行,每行两个整数L、R。

输出格式:

M行,每行一个整数,其中第i行的整数表示第i个询问的答案。

输入输出样例

输入样例#1:

1
2
3
4
5
6
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6

输出样例#1:

1
2
3
4
6
9
5
2

说明

对于全部的数据,1<=N、M、K<=50000

题解

莫队算法的推导我就不推了,毕竟集训的时候也会讲,而且这个也并不重要。

总的来说莫队算法就是一个离线暴力解决区间查询问题的一个优良的暴力算法(注意只是查询)

至于什么是离线呢,就是我们可以等所以的输入全部都输完了然后对一大批输入统一做操作,这种情况就叫做离线啦!

这道题的莫队是裸的不能再裸的了,我们把输入写个分块排个序,然后开辟一个c数组记录一下数字的出现次数,然后跑莫队就行了,裸题实锤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=50010;
typedef long long ll;

int n,m,k;
int pos[maxn],c[maxn],a[maxn];
ll ans[maxn],Ans;

struct node{
int l,r,id;
}q[maxn];

bool cmp(node a,node b){
if(pos[a.l]==pos[b.l])
return a.r<b.r;
else
return pos[a.l]<pos[b.l];
}

void add(int i){
Ans-=c[a[i]]*c[a[i]];
c[a[i]]++;
Ans+=c[a[i]]*c[a[i]];
}

void del(int i){
Ans-=c[a[i]]*c[a[i]];
c[a[i]]--;
Ans+=c[a[i]]*c[a[i]];
}

int main(){
ios::sync_with_stdio(false);
while(cin>>n>>m>>k){
int L=1,R=0;
Ans=0;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
int block=(int)sqrt(n);
for(int i=1;i<=n;i++)
cin>>a[i],pos[i]=i/block;
for(int i=1;i<=m;i++)
cin>>q[i].l>>q[i].r,q[i].id=i;
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;i++){
while(L<q[i].l){
del(L);
L++;
}
while(L>q[i].l){
L--;
add(L);
}
while(R<q[i].r){
R++;
add(R);
}
while(R>q[i].r){
del(R);
R--;
}
ans[q[i].id]=Ans;
}
for(int i=1;i<=m;i++)
cout << ans[i] << endl;
}
return 0;
}