[BZOJ4241]历史研究-回滚莫队

Description

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。

事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:
1. 选择日记中连续的一些天作为分析的时间段
2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
3. 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

Input

第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。

Output

输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度

Sample Input

5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4

Sample Output

9
8
8
16
16

HINT

1<=N<=10^5
1<=Q<=10^5
1<=Xi<=10^9 (1<=i<=N)

Source

JOI 2013~2014 春季training合宿 竞技1 By PoPoQQQ


只有根号算法才能使用的奇妙操作……
这个月终于凑齐四篇博客了不容易啊


思路:
看那个大大的 10 5
基本上不是 O ( n l o g 2 n ) 就是 O ( n n )

由于询问的内容看起来很迷,考虑使用根号算法。
分块当然是可以分块的,但那不是要讨论的重点。

于是有了一个 O ( n n l o g n ) 的莫队算法:
对询问进行莫队式分块,开个桶记录当前区间每种数出现次数,然后移动时维护一个可删除堆来计算答案。

然而这个东西需要松一松卡一卡常。

考虑使用堆的契机:难以在删除后快速维护最大值。也就是说,易于插入,却难以删除。
于是考虑使用这个叫回滚式莫队的东西:

对于左端点在同一块内的询问,考虑不维护整个询问区间,而是仅维护分块区间右端点到询问区间右端点这一段的信息,忽略询问左端点到分块右端点这一段的信息。
而对于每个询问,只需要在维护区间的右端点在该询问的右端点上时,暴力扫一遍询问区间左端点到分块区间右端点处的信息,并计算答案即可。计算完毕后,再将这一段信息删除。

此时的删除只需要将当前区间答案重设为原来左端点在分块区间右端点时的答案,并将新增的桶内值减回去即可,删除单点是 O ( 1 ) 的。
由于每个询问有一次这样的暴力操作,其余部分同原莫队算法,而单次这样的暴力操作扫过的区间长度不超过 O ( n ) ,因此复杂度为 O ( n n )

“人们在压榨着暴力的每一点潜力……”
——来自CF某外国小哥看到题解中的分块做法时发出的感慨……

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N=1e5+9;

struct tri
{
    int a,b,c;
    tri(int _a=0,int _b=0,int _c=0){a=_a;b=_b;c=_c;}
};

tri e[N];
int bel[N];
int n,q,x[N],blk,top;
ll ans[N],cans,recans;
int vis[N],buc[N],val[N],tim;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

inline void write(ll x){if(x>=10)write(x/10);putchar('0'+x%10);}
inline void chkmax(ll &a,ll b){if(a<b)a=b;}
inline int min(int a,int b){return a>b?b:a;}

inline bool cmp(tri x,tri y)
{
    if(bel[x.a]==bel[y.a])
        return x.b<y.b;
    return bel[x.a]<bel[y.a];
}

inline void add(int x)
{
    if(vis[x]!=tim)vis[x]=tim,buc[x]=0;
    buc[x]++;chkmax(cans,(ll)buc[x]*val[x]);
}

int main()
{
    n=read();q=read();
    for(int i=1;i<=n;i++)
        val[++top]=x[i]=read();
    for(int i=1;i<=q;i++)
    {
        e[i].a=read();
        e[i].b=read();
        e[i].c=i;
    }

    blk=sqrt(n);
    for(int i=1;i<=n;i++)
        bel[i]=i/blk;

    sort(e+1,e+q+1,cmp);
    sort(val+1,val+top+1);
    top=unique(val+1,val+top+1)-val-1;
    for(int i=1;i<=n;i++)
        x[i]=lower_bound(val+1,val+top+1,x[i])-val;

    int ptr=1,ed=0;top=0;
    for(int i=0;i<=n/blk;i++)
    {
        ed=min(n,blk*(i+1)-1);
        tim++;int rp=ed;cans=0;
        for(;ptr<=n && bel[e[ptr].a]==i;ptr++)
        {
            while(rp<e[ptr].b)
                add(x[++rp]);
            recans=cans;
            for(int i=min(ed,e[ptr].b);i>=e[ptr].a;i--)
                add(x[i]);
            ans[e[ptr].c]=cans;
            cans=recans;
            for(int i=min(ed,e[ptr].b);i>=e[ptr].a;i--)
                buc[x[i]]--;
        }
    }

    for(int i=1;i<=q;i++)
        write(ans[i]),putchar('\n');
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zlttttt/article/details/80445391