区间求和 (莫队算法 、分块排序)

链接:https://ac.nowcoder.com/acm/contest/1085/G
来源:牛客网
 

区间求和

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

小sun最近突然对区间来了兴趣,现在他有这样一个问题想问问你:
给你n个数,每个数为aia_iai​,现在有m个询问,每个询问l,r,需要求出:
∑i=lrai∗num(ai)\sum_{i=l}^r a_i*num(a_i)∑i=lr​ai​∗num(ai​)

num(ai)num(a_i)num(ai​)代表aia_iai​在这个区间中出现的次数。
你能帮帮他吗?

输入描述:

第一行,两个整数n,m

第二行,总共n个数,代表这个数列

接下来m行,每行两个整数l,r,代表一个询问

输出描述:

输出总共m行,对于每个询问,输出这个询问对应的答案

示例1

输入

复制

10 5
1 3 2 4 5 6 4 5 6 7
1 5
2 5
3 4
1 10
3 7

输出

复制

15
14
6
73
29

备注:

1≤n,m≤1e51\leq n,m\leq 1e51≤n,m≤1e5
1≤ai≤1e51\leq a_i\leq 1e51≤ai​≤1e5
1≤l,r≤n1\leq l,r\leq n1≤l,r≤n

》》》莫队算法

题解:莫队例题 套模板 (最后输出为%lld类型!!)

分块:    return (l/block==S.l/block) ? r<S.r:l<S.l;
       
 

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
ll c[100005];
ll num[100005];
ll block;
ll n,m;
ll ans=0;
ll anss[100005];
struct node
{
	int l,r,id;
	bool operator < (const node & S) const
	{
		return (l/block==S.l/block) ? r<S.r:l<S.l;
		//同块排序 
	}
}a[100005];
int add(int v)
{
	ans-=1ll*c[v]*num[c[v]]*num[c[v]];//!!!
	num[c[v]]++;
	ans+=1ll*c[v]*num[c[v]]*num[c[v]];//!!!
}
int del(int v)
{
	ans-=1ll*c[v]*num[c[v]]*num[c[v]];//!!! 先减后加 
	num[c[v]]--;
	ans+=1ll*c[v]*num[c[v]]*num[c[v]];//!!! 
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].id=i;
	}
	block=sqrt(n);
	sort(a+1,a+1+m);
	int l=0,r=0;
	for(int i=1;i<=m;i++)
	{
		int ql=a[i].l;
		int qr=a[i].r;
		while(ql<l) add(--l);
		while(r<qr) add(++r);
		while(l<ql) del(l++);//!!前置和后置 
		while(qr<r) del(r--);//!!
		anss[a[i].id]=ans;
	}
	for(int i=1;i<=m;i++) printf("%lld\n",anss[i]);
	//最后输出类型为%lld!!!! 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值