loj#2874. 「JOISC 2014 Day1」历史研究

题面


分析

本题可以作为回滚莫队的入门题。回滚莫队是用于解决一类具有莫队特征、且加删点操作有一个很简单而另一个很难的时候可以使用的算法,复杂度同样为根号级。

举例而言,本题题意等价于每次询问给定区间,求区间内每个数的值乘以其出现次数之积的最大值。显然,这个问题中加点操作很简单,而朴素的删点需要反复维护次大值,不可做。

对于这类型的题目,我们可以思考如何尽量执行加点操作而不执行删点操作。可以想到,询问分块后按照区间左端点所在块为第一关键字,右端点为第二关键字排序。

对于每个询问:

  • 如果左右端点在同一个块内,我们可以暴力计算,复杂度显然是根号。

  • 否则,分别考虑左右端点的处理。对于右端点,由于同块内有序,所以可以直接加点。对于左端点,由于可能无序,所以每次都从块的右端点开始向左端点加点。每个询问的左端点最多可以加根号次,而右端点总共加\(n\)次,所以最后的复杂度仍然是\(n\sqrt{n}\)的。

于是我们就解决了这类型的问题。需要注意的是,回滚莫队的排序方式影响正确性,而普通莫队的排序只影响时间。

代码

/*
By Nero Claudius Caeser Augustus Germanicus,
Imperatorum Romanorum.
*/
#include <bits/stdc++.h>

using namespace std;

namespace StandardIO{

	template<typename T>void read(T &x){
		x=0;T f=1;char c=getchar();
		for(; c<'0'||c>'9'; c=getchar()) if(c=='-') f=-1;
		for(; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}
	template<typename T>void write(T x){
		if(x<0) putchar('-'),x*=-1;
		if(x>=10) write(x/10);
		putchar(x%10+'0');
	}

} using namespace StandardIO;

namespace Project{
#define int long long	
	const int N=100000+100;
	
	int n,q,block;
	int a[N],b[N],belong[N];
	struct node{
		int l,r,id;
		bool operator < (const node x)const{
			return (belong[l]==belong[x.l])?r<x.r:(belong[l]<belong[x.l]);
		}
	} ask[N];
	int cnt[N],ans[N],ver=1,cnt2[N],res;
	
	int calc(int l,int r){
		int res=0;
		for(int i=l; i<=r; ++i) cnt2[a[i]]=0;
		for(int i=l; i<=r; ++i){
			++cnt2[a[i]];
			res=max(res,cnt2[a[i]]*b[a[i]]);
		}
		return res;
	}
	void add(int x){
		++cnt[a[x]];
		res=max(res,cnt[a[x]]*b[a[x]]);
	} 
	void solve(int x){
		int rr=min(x*block,n),qr=rr;res=0;
		memset(cnt,0,sizeof(cnt));
		while(belong[ask[ver].l]==x){
			if(belong[ask[ver].l]==belong[ask[ver].r]){
				ans[ask[ver].id]=calc(ask[ver].l,ask[ver].r),++ver;continue;
			}
			while(qr<ask[ver].r) add(++qr);
			int last=res;
			for(int i=ask[ver].l; i<=rr; ++i) add(i);
			ans[ask[ver].id]=res;
			for(int i=ask[ver].l; i<=rr; ++i) --cnt[a[i]];
			res=last;
			++ver;
		}
	}
	
	void MAIN(){
		read(n),read(q),block=sqrt(n);
		for(int i=1; i<=n; ++i){
			read(a[i]),b[i]=a[i];
		}
		sort(b+1,b+n+1);int len=unique(b+1,b+n+1)-b-1;
		for(int i=1; i<=n; ++i){
			a[i]=lower_bound(b+1,b+len+1,a[i])-b;
			belong[i]=(i-1)/block+1;
		}
		for(int i=1; i<=q; ++i){
			read(ask[i].l),read(ask[i].r),ask[i].id=i;
		}
		sort(ask+1,ask+q+1);
		for(int i=1; i<=belong[n]; ++i){
			solve(i);
		}
		for(int i=1; i<=q; ++i){
			write(ans[i]),puts("");
		}
	}
#undef int 
}

int main(){ 
	Project::MAIN();
}
posted @ 2020-08-01 22:47  Ilverene  阅读(335)  评论(0编辑  收藏  举报