洛谷 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
",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
",ans[i]);
	
    return 0;
}

原文地址:https://www.cnblogs.com/zzhzzh123/p/12240514.html