欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BZOJ 4241 历史研究

程序员文章站 2022-03-03 14:46:42
...

BZOJ 4214

http://www.lydsy.com/JudgeOnline/problem.php?id=4241

区间询问。

询问元素大小与出现次数乘积的最大值。

借此学习回滚莫队。

经典的莫队:对于区间[L,R]向别的区间转移状态时。有时是删除。有时是插入。同时需要维护。这类问题需要插入和删除,以及维护比较快的情况下。才可以实现。

回滚莫队:插入和删除是互反的操作。对于有些问题。插入和删除好办。维护困难。回滚莫队可以解决:插入维护时困难,或者,删除维护时困难的问题。(必须有一个容易办到)

这里回滚模队意思应该是保留了某个区间的状态。

更直白的说。插入和删除简单。某个操作的维护不简单。

对于那个操作。我们也不打算维护。而是记录。

我们对序列按m分块后。形成
[km,km+m]

的区间。

我们记询问{Li,Ri1ir}有:
Li,Ri,li[km,km+m],Ri>km+m

我们对区间分开维护:

对于插入简单,且插入维护简单的问题。

每次只维护
[km+m+1,Ri]

对于[li,km+m],直接暴力插入。求解。然后删除(不维护的删除,将[li,km+m]产生的影响消去。这就需要我们记录[km+m+1,Ri]的状态了。)

相反。对于删除简单。可以倒着来。

T(n)=O(nn)

这个题目可以说上面思想的模版题了。

#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#define MAXN 100005

using namespace std;

typedef long long LL;
const char l='0'-1;
const char r='9'+1;
struct Io
{
    char A[MAXN],*pb,*pe;
    void Io_fread()
    {
        pe=A+fread(A,1,MAXN,stdin);
        pb=A;
    }
    Io()
    {
        pe=pb=A;
    }
    int read()
    {
        if(pb==pe)
        {
            Io_fread();
            if(pb==pe)return 0;
        }
        while(*pb==' '||*pb=='\n'||*pb=='\r')
        {
            pb++;
            if(pb==pe)
            {
                Io_fread();
                if(pb==pe)return 0;
            }
        }
        int ans=0;
        while(*pb>l&&*pb<r)
        {
            ans=ans*10+(*pb-'0');
            pb++;
            if(pb==pe)
            {
                Io_fread();
                if(pb==pe)return ans;
            }
        }
        return ans;
    }
}I;
struct node
{
    int l,r,L,id;
    node(){}
    bool operator <(const node &A)const
    {
        if(L!=A.L)return L<A.L;
        return r<A.r;
    }
}Q[MAXN];

LL ans[MAXN];
int X[MAXN];
int tmp[MAXN];
LL cnt[MAXN];
int vis[MAXN];
int main ()
{
    //freopen("/Users/gaoxusheng/Desktop/In.txt","r",stdin);
    int n,q,m;
    n=I.read();
    q=I.read();
    m=sqrt(n+10);
    for(int i=0;i<n;i++)
    {
        tmp[i]=I.read();
        X[i+1]=tmp[i];
    }
    for(int i=0;i<q;i++)
    {
        Q[i].l=I.read();
        Q[i].r=I.read();
        Q[i].L=Q[i].l-Q[i].l%m+m;
        Q[i].id=i;
    }
    sort(Q,Q+q);
    sort(tmp,tmp+n);
    int size=(int)(unique(tmp,tmp+n)-tmp);
    for(int i=1;i<=n;i++)   X[i]=(int)(lower_bound(tmp,tmp+size,X[i])-tmp);
    int v=0,L=-1,R=-1;
    LL maxn=0;
    for(int i=0;i<q;i++)
    {
        if(L!=Q[i].L)
        {
            v++;
            maxn=0;
            R=Q[i].L-1;
            L=Q[i].L;
        }
        while(R<Q[i].r)
        {
            R++;
            if(vis[X[R]]<v)cnt[X[R]]=0;
            vis[X[R]]=v;
            cnt[X[R]]+=tmp[X[R]];
            if(cnt[X[R]]>maxn)maxn=cnt[X[R]];
        }
        LL bu=maxn;
        int s=min(L,Q[i].r+1);
        for(int j=Q[i].l;j<s;j++)
        {
            if(vis[X[j]]<v)cnt[X[j]]=0;
            vis[X[j]]=v;
            cnt[X[j]]+=tmp[X[j]];
            if(cnt[X[j]]>bu)bu=cnt[X[j]];
        }
        ans[Q[i].id]=bu;
        for(int j=Q[i].l;j<s;j++)   cnt[X[j]]-=tmp[X[j]];
    }
    for(int i=0;i<q;i++) printf("%lld\n",ans[i]);
    return 0;
}

相关标签: 离线算法