(CodeForces - 86D)Powerful array(莫队)

题目链接: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;
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值