洛谷 P2709 小B的询问
0x00 思路
题面告诉我们答案就是每种数数量的平方和,因此每种数可以单独计算,用\(cnt\)数组统计每种数的数量
考虑维护平方和\(sum\),如果每种数每次增加/减少整数个,则可以用数学方法维护\(sum\)
我们不能暴力枚举区间内所有的数,复杂度不行
区间问题利器线段树又不能用,因为不符合区间加法的性质,我们考虑莫队
0x01 莫队思路
见洛谷 P1494 [国家集训队]小Z的袜子 /【模板】莫队
这里给出部分代码:
while(L>ql) --L,sum+=1+(cnt[a[L]]<<1),++cnt[a[L]];
while(L<ql) --cnt[a[L]],sum-=1+(cnt[a[L]]<<1),++L;
while(R>qr) --cnt[a[R]],sum-=1+(cnt[a[R]]<<1),--R;
while(R<qr) ++R,sum+=1+(cnt[a[R]]<<1),++cnt[a[R]];
话说关于莫队本身,真没什么好讲,维护才是核心
0x02 Code
#include<bits/stdc++.h>
using namespace std;
#define N 50050
int read(){
int x=0; char c=getchar(); int flag=1;
while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
return x*flag;
}
int n,m,k,a[N],cnt[N];
long long ans[N];
struct node{
int l,r,pos,id;
}q[N];
bool cmp(node a,node b){
return (a.pos^b.pos)?(a.pos<b.pos):((a.pos&1)?(a.r<b.r):(a.r>b.r));
}
signed main(){
n=read(),m=read(),k=read();
int size=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++){
q[i].l=read(),q[i].r=read();
q[i].pos=(q[i].l-1)/size+1;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int L=1,R=0;
long long sum=0;
for(int i=1;i<=m;i++){
int ql=q[i].l,qr=q[i].r;
//printf("%d-%d\n",ql,qr);
while(L>ql) --L,sum+=1+(cnt[a[L]]<<1),++cnt[a[L]];
while(L<ql) --cnt[a[L]],sum-=1+(cnt[a[L]]<<1),++L;
while(R>qr) --cnt[a[R]],sum-=1+(cnt[a[R]]<<1),--R;
while(R<qr) ++R,sum+=1+(cnt[a[R]]<<1),++cnt[a[R]];
ans[q[i].id]=sum;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}