题目链接:https://vjudge.net/problem/CodeForces-86D
题意:给你一连串的数,有很多的询问区间,记一个区间的一个数(x)的个数为num,求num*num*x。
思路:这种涉及很多区间中数字个数统计的查询,并且不涉及区间的修改操作时,可以直接用莫队算法。
莫队的核心就是充分利用上一次的查询结果(其实就是通过上一次查询后的cnt数组的变化,充分利用上一次询问的结果,得到本次询问的结果),由于这里是平方,所以需要稍稍转换一下,当cnt从n变为n+1,它的平方实际上就是增加了(2*n+1),【(n+1)^2-n^2=2*n+1】,同理,当cnt从n变为n-1,就是减少了2*n-1,那么每一次的区间询问结果与上一次询问的结果,就相差(cnt的变化量*这个数字的值),这样只要相应的变化上次询问的结果就行。
写题过程:一开始没用读入挂,交了一发,在test6超时了,然后用了读入挂,还是超时,最后才发现,是重载'<'时,我用的if...else...表达式,改成条件运算符就过了,不明所以。。。在看别人的代码时,发现可以不用专门写一个分块函数以及分块数组,直接用一个unit就行,以后可以采用这种方法,尽管这个比我用分块函数以及分块数组的代码慢了46Ms,不过可以接受啦,毕竟代码比较简洁。
这些都在下面的代码中有注释,可以参看。
代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin freopen("in.txt","r",stdin)
#define Fout freopen("out.txt","w",stdout)
#define Case(T) int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b) for(int i = a; i < b; ++i)
#define fd(i,a,b) for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val) fill(a,a+n,val)
#define Scand(n) scanf("%d",&n)
#define Scand2(a,b) scanf("%d%d",&a,&b)
#define Scand3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s) scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 200000 + 50;
const int INF = 0xffffff;
#ifndef ONLINE_JUDGE
#endif // ONLINE_JUDGE
inline int read(){
int sgn = 1; int sum = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
if(ch == '-') sgn = -sgn;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
sum = sum*10+(ch-'0');
ch = getchar();
}
return sgn*sum;
}
int n,t;
int arr[maxn];
int cnt[10000005], id[maxn];
ll ans[maxn];
int L,R;
int unit;
ll now;
struct range{
int l,r;
int askorder;
friend bool operator < (const range &a, const range &b){
// if(id[a.l] == id[a.l]) //①这种if...else...写法会超时
// return a.r < b.r;
// return id[a.l] < id[b.l];
// return id[a.l] == id[b.l] ? a.r < b.r : id[a.l] < id[b.l]; //②这个得开一个id数组,但是很快
return a.l/unit != b.l/unit ? a.l/unit < b.l/unit : a.r < b.r; //③这个不用开数组,只要求出unit的值就行,比②略慢一点点
}
}rangee[maxn];
void blocker(){
int k = sqrt(n);
fo(i, 1, n+1)
id[i] = (i-1)/k+1;
}
void move(int x, int type){
if(type == 0){
now = now-((cnt[arr[x]]<<1)-1)*(ll)arr[x];
cnt[arr[x]]--;
}else if(type == 1){
now = now+((cnt[arr[x]]<<1)|1)*(ll)arr[x];
cnt[arr[x]]++;
}
}
int main()
{
#ifndef ONLINE_JUDGE
//Fin;
#endif // ONLINE_JUDGE
n = read(); t = read();
fo(i, 1, n+1)
arr[i] = read();
// blocker();
unit = sqrt(n);
for(int i = 1; i <= t; ++i){
rangee[i].l = read();
rangee[i].r = read();
rangee[i].askorder = i;
}
sort(rangee+1, rangee+t+1);
L = 0; R = 0; now = 0;
for(int i = 1; i <= t; ++i){
while (L < rangee[i].l) {
move(L++,0);
}
while (R > rangee[i].r) {
move(R--,0);
}
while (L > rangee[i].l) {
move(--L,1);
}
while (R < rangee[i].r) {
move(++R,1);
}
ans[rangee[i].askorder] = now;
}
for(int i = 1;i <= t; ++i)
{
printf("%lld\n",ans[i]);
}
return 0;
}