【BZOJ4241】历史研究-回滚莫队

测试地址:历史研究
做法:本题需要用到回滚莫队。
看到这道题目,感觉主席树、树套树这种都不能做,于是考虑离线的莫队算法。
然而我们很快发现一个问题:对于题目中要求的信息,由于在插入时,max值要么不变,要么变成你目前插入的元素所对应的重要度,所以这个很容易完成。但是删除时就不一定了,你并不知道max值此时会怎么变化。这就意味着我们不能用传统的莫队算法解决这一问题,而需要使用一种叫回滚莫队的方法规避删除操作。
回滚莫队中对询问排序的方法和传统莫队仍然一样,都是以左端点所在的块为第一关键字,以右端点为第二关键字排序。接下来我们分类讨论:
首先,对于左右端点在同一块内的询问,显然可以直接O(n)求出答案。
然后,对于左右端点不在同一块内的询问,我们每次处理左端点在当前块内的询问。因为左右端点不在同一块中,因此区间会被当前块的右边界分割成两段。又因为右端点已经是有序的了,因此我们可以轻易计算右边那段的信息,显然对于每一块最多插入O(n)次。而对于每个询问,我们直接进行插入来计算整段的信息(从右边段开始扩展)。处理完这个询问后,就把往左的插入操作依次撤销(也就是“回滚”),回到只剩下右边段信息的状态,因此每个询问回滚的复杂度是O(n)的。这样总的时间复杂度还是O(nn),但规避了删除操作,于是利用这个思想就可以解决这一题了。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,tot,a[100010],blocksiz;
ll val[100010],cnt[100010]={0},excnt[100010]={0},ans[100010];
struct forsort
{
    int id;
    ll val;
}f[100010];
struct Query
{
    int l,r,id;
}q[100010];

bool cmp(forsort a,forsort b)
{
    return a.val<b.val;
}

bool cmpq(Query a,Query b)
{
    if (a.l/blocksiz!=b.l/blocksiz)
        return a.l/blocksiz<b.l/blocksiz;
    else return a.r<b.r;
}

void Mo()
{
    blocksiz=(int)(sqrt(n)+1);
    sort(q+1,q+m+1,cmpq);
    int now=0,l,r=(now+1)*blocksiz-1;
    ll mx=0;
    for(int i=1;i<=m;i++)
    {
        if (now<q[i].l/blocksiz)
        {
            now=q[i].l/blocksiz;
            mx=0,r=(now+1)*blocksiz-1;
            memset(cnt,0,sizeof(cnt));
        }
        if (q[i].l/blocksiz==q[i].r/blocksiz)
        {
            l=q[i].l,r=l-1;
            ans[q[i].id]=0;
            while(r<q[i].r)
            {
                r++;
                cnt[a[r]]++;
                ans[q[i].id]=max(ans[q[i].id],val[a[r]]*cnt[a[r]]);
            }
            r=l-1;
            while(r<q[i].r) r++,cnt[a[r]]--;
            r=(now+1)*blocksiz-1;
        }
        else
        {
            l=(now+1)*blocksiz;
            while(r<q[i].r)
            {
                r++;
                cnt[a[r]]++;
                mx=max(mx,val[a[r]]*cnt[a[r]]);
            }
            ans[q[i].id]=mx;
            while(l>q[i].l)
            {
                l--;
                excnt[a[l]]++;
                ans[q[i].id]=max(ans[q[i].id],val[a[l]]*(cnt[a[l]]+excnt[a[l]]));
            }
            l=(now+1)*blocksiz;
            while(l>q[i].l) l--,excnt[a[l]]--;
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&f[i].val);
        f[i].id=i;
    }
    sort(f+1,f+n+1,cmp);
    tot=0;
    for(int i=1;i<=n;i++)
    {
        if (i==1||f[i].val!=f[i-1].val)
            val[++tot]=f[i].val;
        a[f[i].id]=tot;
    }

    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    Mo();
    for(int i=1;i<=m;i++)
        printf("%lld
",ans[i]);

    return 0;
}
原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793263.html