[BZOJ4241]历史研究

bzoj

description

一个序列\(a_i\),定义一个区间的价值为这个区间内的一个元素乘上这个元素在这个区间内的出现次数的最大值。
多次询问某个区间的价值。
\(n,m\le10^5\)

sol

发现莫队不方便快速维护这个东西(删除的时候没法维护最大值)。
考虑分块。设\(ans_{i,j}\)表示第\(i\)个块到第\(j\)个块的答案,\(s_{i,j}\)表示前\(i\)个块中\(j\)这个数的出现次数。
整块部分直接用答案,对于不是不是整块的部分,开一个临时桶,先把所以在非整块中出现过的数字的出现次数蒯到这个桶里(这一步的复杂度是\(O(\sqrt n)\)的),在暴力扫一遍更新答案。
时间复杂度和空间复杂度都是\(O(n\sqrt n)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
#define ll long long
const int N = 1e5+5;
const int M = 350;
int n,q,a[N],o[N],len,block,m,pos[N],L[M],R[M],sum[M][N],tmp[N];
ll ans[M][M];
void init(){
	block=sqrt(n);m=(n-1)/block+1;
	for (int i=1;i<=n;++i) pos[i]=(i-1)/block+1;
	for (int i=1;i<=m;++i) L[i]=(i-1)*block+1,R[i]=i*block;
	R[m]=n;
	for (int i=1;i<=m;++i){
		ll res=0;
		for (int j=L[i];j<=n;++j){
			++tmp[a[j]],res=max(res,1ll*o[a[j]]*tmp[a[j]]);
			if (j==R[pos[j]]) ans[i][pos[j]]=res;
		}
		memset(tmp,0,sizeof(tmp));
	}
	for (int i=1;i<=n;++i) ++sum[pos[i]][a[i]];
	for (int i=1;i<=m;++i)
		for (int j=1;j<=n;++j)
			sum[i][j]+=sum[i-1][j];
}
ll query(int l,int r){
	ll res=0;
	if (pos[r]-pos[l]<=1){
		for (int i=l;i<=r;++i)
			++tmp[a[i]],res=max(res,1ll*o[a[i]]*tmp[a[i]]);
		for (int i=l;i<=r;++i) tmp[a[i]]=0;
		return res;
	}else{
		res=ans[pos[l]+1][pos[r]-1];
		for (int i=l;i<=R[pos[l]];++i){
			if (!tmp[a[i]]) tmp[a[i]]=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
			++tmp[a[i]],res=max(res,1ll*o[a[i]]*tmp[a[i]]);
		}
		for (int i=L[pos[r]];i<=r;++i){
			if (!tmp[a[i]]) tmp[a[i]]=sum[pos[r]-1][a[i]]-sum[pos[l]][a[i]];
			++tmp[a[i]],res=max(res,1ll*o[a[i]]*tmp[a[i]]);
		}
		for (int i=l;i<=R[pos[l]];++i) tmp[a[i]]=0;
		for (int i=L[pos[r]];i<=r;++i) tmp[a[i]]=0;
		return res;
	}
}
int main(){
	n=gi();q=gi();
	for (int i=1;i<=n;++i) a[i]=o[i]=gi();
	sort(o+1,o+n+1);len=unique(o+1,o+n+1)-o-1;
	for (int i=1;i<=n;++i) a[i]=lower_bound(o+1,o+len+1,a[i])-o;
	init();
	while (q--){
		int l=gi(),r=gi();
		printf("%lld\n",query(l,r));
	}
	return 0;
}
posted @ 2018-06-27 22:13  租酥雨  阅读(198)  评论(0编辑  收藏  举报