BZOJ4241: 历史研究 分块

本文介绍了一种利用分块技巧解决区间查询问题的方法。给定一个序列,通过预处理将序列分成若干块,并建立相应的数据结构来快速响应区间内元素的最大价值查询。这种方法适用于需要频繁进行区间查询的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给你一个序列,对于一个区间,某一个值的价值为这个值出现的次数*这个值的权值,每次给一个询问,问这个区间内最大的价值。


Sample Input
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4


Sample Output
9
8
8
16
16


分块套路题,没什么好说的。。。


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

bool v[110000];
int a[110000], b[110000], c[110000], d[110000];
int sum[320][110000], belong[110000], ll[320], rr[320];
int s[110000]; LL f[320][320];

int main() {
	int n = read(), m = read();
	for(int i = 1; i <= n; i++) a[i] = read(), b[i] = a[i];
	sort(b + 1, b + n + 1);
	for(int i = 1; i <= n; i++) c[i] = lower_bound(b + 1, b + n + 1, a[i]) - b, d[c[i]] = a[i];
	int p = sqrt(n);
	for(int i = 1; i <= n; i++) {
		belong[i] = (i - 1) / p + 1;
		if(belong[i] != belong[i - 1]) ll[belong[i]] = i, rr[belong[i - 1]] = i - 1;
	} ll[1] = 1, rr[belong[n]] = n;
	for(int i = 1; i <= belong[n]; i++) {
		memset(s, 0, sizeof(s)); LL ans = 0;
		for(int j = i; j <= belong[n]; j++) {
			for(int k = ll[j]; k <= rr[j]; k++) {
				s[c[k]]++;
				if((LL)s[c[k]] * a[k] > ans) ans = (LL)s[c[k]] * a[k];
			} f[i][j] = ans;
		}
		for(int j = ll[i]; j <= rr[i]; j++) 
		sum[i][c[j]]++;
	} memset(s, 0, sizeof(s));
	for(int i = 1; i <= belong[n]; i++) for(int j = 1; j <= n; j++) sum[i][j] += sum[i - 1][j];
	for(int i = 1; i <= m; i++) {
		int l = read(), r = read();
		int bl = belong[l], br = belong[r];
		LL ans = 0;
		if(bl == br) {
			for(int j = l; j <= r; j++) {
				s[c[j]]++;
				if((LL)s[c[j]] * a[j] > ans) ans = (LL)s[c[j]] * a[j];
			} printf("%lld\n", ans);
			for(int j = l; j <= r; j++) s[c[j]] = 0;
			continue;
		} ans = f[bl + 1][br - 1];
		for(int j = l; j <= rr[bl]; j++) {
			s[c[j]]++;
			if(!v[c[j]]) {
				v[c[j]] = 1;
				s[c[j]] += sum[br - 1][c[j]] - sum[bl][c[j]];
			} if((LL)s[c[j]] * a[j] > ans) ans = (LL)s[c[j]] * a[j];
		}
		for(int j = ll[br]; j <= r; j++) {
			s[c[j]]++;
			if(!v[c[j]]) {
				v[c[j]] = 1;
				s[c[j]] += sum[br - 1][c[j]] - sum[bl][c[j]];
			} if((LL)s[c[j]] * a[j] > ans) ans = (LL)s[c[j]] * a[j];
		} printf("%lld\n", ans);
		for(int j = l; j <= rr[bl]; j++) s[c[j]] = 0, v[c[j]] = 0;
		for(int j = ll[br]; j <= r; j++) s[c[j]] = 0, v[c[j]] = 0;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值