习题:历史研究(回滚莫队)

题目

传送门

思路

很版的一道回滚莫队的题

我们如果用普通的莫队,我们发现最难维护的是最大值,

因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护

时间复杂度为\(O(n*log_n*\sqrt n)\)

但是我们想,我们如果已知一个莫队的左端点和右端点以及它的最大值

那么这个莫队向外拓展我们是很容易维护的

之后如果下一个操作也是向外拓展就向外拓展,如果是内缩,

我们就将这个莫队还原成为我们最开始已知的样子,在进行拓展

这也就是回滚莫队的主要思想,这道题也是如此

时间复杂度依然也是\(O(n*\sqrt n)\)

代码

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int bel[100005];
int n,q;
int sn;
int lenv;
int now;
long long anss;
int a[100005];
int v[100005];
int hashh[100005];
int t[100005];
long long ans[100005];
struct node
{
	int l;
	int r;
	int id;
	friend bool operator < (const node &a,const node &b)
	{
		if(bel[a.l]==bel[b.l])
			return a.r<b.r;
		return a.l<b.l;
	}
}p[100005];
void add(int pos)
{
	t[hashh[pos]]++;
	anss=max(anss,1ll*t[hashh[pos]]*a[pos]);
}
void sub(int pos)
{
	t[hashh[pos]]--;
}
long long sum(int l,int r)
{
	long long ret=0;
	int t[100005]={};
	for(int i=l;i<=r;i++)
	{
		t[hashh[i]]++;
		ret=max(ret,1ll*t[hashh[i]]*a[i]);
	}
	return ret;
}
void solve(int id)
{
	int l=id*sn+1;
	int r=l-1;
	memset(t,0,sizeof(t));
	anss=0;
	while(bel[p[now].l]==id)
	{
		if(bel[p[now].l]==bel[p[now].r])
		{
			ans[p[now].id]=sum(p[now].l,p[now].r);
			now++;
			continue;
		}
		while(r<p[now].r)
			add(++r);
		long long tmp=anss;
		while(l>p[now].l)
			add(--l);
		ans[p[now].id]=anss;
		while(l<min(id*sn,n)+1)
			sub(l++);
		anss=tmp;
		now++;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>q;
	sn=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		v[i]=a[i];
		bel[i]=(i-1)/sn+1;
	}
	sort(v+1,v+n+1);
	lenv=unique(v+1,v+n+1)-v-1;
	for(int i=1;i<=n;i++)
		hashh[i]=lower_bound(v+1,v+lenv+1,a[i])-v;
	for(int i=1;i<=q;i++)
	{
		cin>>p[i].l>>p[i].r;
		if(p[i].l>p[i].r)
			swap(p[i].l,p[i].r);
		p[i].id=i;
	}
	sort(p+1,p+q+1);
	now=1;
	for(int i=1;i<=bel[n];i++)
		solve(i);
	for(int i=1;i<=q;i++)
		cout<<ans[i]<<'\n';
	return 0;
}

posted @ 2020-01-04 14:59  loney_s  阅读(159)  评论(0)    收藏  举报