• 【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;
    }
  • 相关阅读:
    MeshLab编译理解
    几个经典的数学库之一学习---VCGlib(1)
    开源协议的几种类型认识
    Win & Mac 系统之间U盘传递的U盘文件格式选取问题
    用VS2010编译python2.7的源码
    类之间关系理解:组合>聚合>关联>依赖;实现,继承
    Linux进阶:让效率翻倍的Bash技巧(一)
    Linux下nc命来实现文件传输
    nginx 405 not allowed问题的解决
    vim同时打开多个文件进行编辑
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793263.html
Copyright © 2020-2023  润新知