#牛客网 小白月赛17 G 区间求和 (莫队)

 

题目描述

小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

题目大意 : 输入长度为 N 的序列, M次询问, 每次问从 L 到 R当中, a【i】 * (a【i】的出现次数)之和

思路 : 很裸的莫队, 每次扩大区间时, 减去上一次更新的值, 再加上当前更新的某个数的出现次数的平方就好, 这个结论不难推

Accepted code

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

struct node
{
	int l, r, x;
}e[MAXN << 1];
int p[MAXN], belong[MAXN], n, m;
int num[MAXN], sum, L = 1, R;
ll pre[MAXN], ans;
bool cmp(node a, node b) {   // 分块排序
	if (belong[a.l] == belong[b.l]) return a.r < b.r;
	return belong[a.l] < belong[b.l];
}
void add(int x) { 
	ans -= num[p[x]] * num[p[x]] * p[x];
	num[p[x]]++, ans += num[p[x]] * num[p[x]] * p[x];
}
void del(int x){
	ans -= num[p[x]] * num[p[x]] * p[x];
	num[p[x]]--, ans += num[p[x]] * num[p[x]] * p[x];
}

int main()
{
	cin >> n >> m; sum = sqrt(n);
	int sz = ((double)n / sum);
	for (int i = 1; i <= sz; i++) {
		for (int j = (i - 1) * sum + 1; j <= i * sum; j++)
			belong[j] = i;
	}
	for (int i = 1; i <= n; i++) sc("%d", &p[i]);
	for (int i = 0; i < m; i++) sc("%d %d", &e[i].l, &e[i].r), e[i].x = i;
	sort(e, e + m, cmp);
	for (int i = 0; i < m; i++) {
		int l = e[i].l, r = e[i].r, id = e[i].x;
		while (L < l) del(L++);
		while (L > l) add(--L);
		while (R < r) add(++R);
		while (R > r) del(R--);
		pre[id] = ans;
	}
	for (int i = 0; i < m; i++) printf("%lld\n", pre[i]); 
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值