习题:历史研究(回滚莫队)
Posted loney-s
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了习题:历史研究(回滚莫队)相关的知识,希望对你有一定的参考价值。
题目
思路
很版的一道回滚莫队的题
我们如果用普通的莫队,我们发现最难维护的是最大值,
因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护
时间复杂度为(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]<<'
';
return 0;
}
以上是关于习题:历史研究(回滚莫队)的主要内容,如果未能解决你的问题,请参考以下文章