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

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;
}

以上是关于习题:历史研究(回滚莫队)的主要内容,如果未能解决你的问题,请参考以下文章

历史研究(回滚莫队)(add)

AT1219 歴史の研究(回滚莫队)

bzoj 4241历史的研究

回滚莫队

回滚莫队的技巧

「AtCoder 1219」歴史の研究