题目链接:Problem - 86D - Codeforces
题目大意:
给定一个长度N的数组a
要求:询问区间1<=L<=R<=N中,每个数字出现次数的平方与当前数字的乘积和。
这道题目也是一道利用莫队思想解决的问题,之前我在介绍莫队算法的时候用了一个例题,就是让我们求区间[ l , r ]中每个不同的数出现次数的平方和,而这个题目只是在那道题目的基础上多了一个乘以当前数字的数值而已。下面先附上那个博客的地址:
(39条消息) 莫队(离线处理区间询问)_AC__dream的博客-CSDN博客
下面来对这道题目进行分析:
先来说下add函数:
我们考虑一下,假如当前加入的数的值为x,我们会对答案造成什么影响呢?首先对于除了x以外的值的贡献没有任何影响,只是影响了x的贡献而已,x的贡献原来是cnt[x]*cnt[x]*x,现在变成了(cnt[x]+1)*(cnt[x]+1)*x,写到这我们显然可以通过o(1)来更新x的贡献。
下面说一下sub函数:
加入当前删除的数为x,与add函数的思考方式相同,当前操作的数值只会影响当前操作的数的贡献,而对其他的数的贡献没有任何贡献,x的贡献原来是cnt[x]*cnt[x]*x,现在变成了(cnt[x]-1)*(cnt[x]-1)*x,同样我们可以通过o(1)来更新x的贡献。
剩下的就是莫队的模板了:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+10;
struct node{
ll l,r,pl,id;
}p[N];
bool cmp(node a,node b)
{
if(a.pl!=b.pl) return a.pl<b.pl;
return a.r<b.r;
}
ll sum[N],a[N],ans,cnt[N];
void add(ll x)
{
ans-=cnt[x]*cnt[x]*x;//x的原来贡献
cnt[x]++;
ans+=cnt[x]*cnt[x]*x;//x的现在贡献
}
void sub(ll x)
{
ans-=cnt[x]*cnt[x]*x;//x的原来贡献
cnt[x]--;
ans+=cnt[x]*cnt[x]*x;//x的现在贡献
}
int main()
{
ll n,q;
scanf("%lld%lld",&n,&q);
ll pl=sqrt(n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=q;i++)
{
scanf("%lld%lld",&p[i].l,&p[i].r);
p[i].id=i;
p[i].pl=(p[i].l-1)/pl+1;
}
sort(p+1,p+q+1,cmp);
ll l=1,r=0;
for(int i=1;i<=q;i++)
{
while(l<p[i].l)
{
sub(a[l]);
l++;
}
while(l>p[i].l)
{
l--;
add(a[l]);
}
while(r<p[i].r)
{
r++;
add(a[r]);
}
while(r>p[i].r)
{
sub(a[r]);
r--;
}
sum[p[i].id]=ans;
}
for(int i=1;i<=q;i++)
printf("%lld\n",sum[i]);
return 0;
}