BZOJ 4241 历史研究

BZOJ 4214

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

区间询问。

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

借此学习回滚莫队。
经典的莫队:对于区间 [L,R] 向别的区间转移状态时。有时是删除。有时是插入。同时需要维护。这类问题需要插入和删除,以及维护比较快的情况下。才可以实现。
回滚莫队:插入和删除是互反的操作。对于有些问题。插入和删除好办。维护困难。回滚莫队可以解决:插入维护时困难,或者,删除维护时困难的问题。(必须有一个容易办到)
这里回滚模队意思应该是保留了某个区间的状态。
更直白的说。插入和删除简单。某个操作的维护不简单。
对于那个操作。我们也不打算维护。而是记录。
我们对序列按 m 分块后。形成
[km,km+m]
的区间。

<script type="math/tex; mode=display" id="MathJax-Element-133"></script>

我们记询问 {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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值