• 【BZOJ4540】序列(HNOI2016)-莫队算法+RMQ


    测试地址:序列
    做法:本题需要用到莫队算法+RMQ。
    首先看到询问不强制在线,并且没有修改,显然非常莫队,那么就来到了这道题的难点:如何处理区间扩张或收缩时答案的变化。
    我们发现往区间中增加或减少一个元素,实际上就是添加或减少以这个元素为开头或结尾的一些子段,显然左右是对称的,所以下面只考虑在区间右端增减元素的情况。我们令f(i,j)为添加子段[i,j],[i+1,j],...,[j,j]能得到的贡献,再令lefti为第i个元素左边第一个比它小的元素的位置,则有:
    f(i,j)=f(i,leftj)+(jleftj)aj
    注意到f(i,j)的贡献就是由一段一段像(jleftj)aj这样的贡献拼起来的,我们可以把这个看成从jleftj连了一条边权为(jleftj)aj的边,显然这样连出来的是一棵树,那么我们要求的贡献实际上就相当于,从第j个元素一直往上跳,一边跳一边累计边权,一直到它指向的元素在序列中的位置小于i为止,这个时候还剩下一些贡献,显然最后停下来的节点的值为区间中的最小值,那么最后区间还有多少个元素,就有多少个这样的贡献,直接RMQ并预处理树上的前缀和即可做到O(1)查询,上面提到的lefti用单调栈预处理一下即可。
    于是我们就解决了这一题,时间复杂度为O(nn)
    (话说我今天才知道RMQ可以做到O(1)查询……我可能是太菜了吧)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,q,st[100010],top,l[100010],r[100010];
    int blocksiz,nowl,nowr,len[100010]={0},mnp[100010][21];
    ll a[100010],mn[100010][21]={0},lft[100010],rht[100010];
    ll ans,Ans[100010];
    struct query
    {
        int l,r,id;
    }Q[100010];
    
    bool cmp(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 init()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
    
        top=0;
        for(int i=1;i<=n;i++)
        {
            l[i]=i;
            while(top>0&&a[st[top]]>a[i])
            {
                r[st[top]]=i-1;
                l[i]=l[st[top]];
                top--;
            }
            st[++top]=i;
        }
        while(top)
        {
            r[st[top]]=n;
            top--;
        }
    }
    
    void init_rmq()
    {
        for(int i=1;i<=n;i++)
            mn[i][0]=a[i],mnp[i][0]=i;
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
            {
                if (j+(1<<(i-1))-1>=n)
                {
                    mn[j][i]=mn[j][i-1];
                    mnp[j][i]=mnp[j][i-1];
                    continue;
                }
                mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
                if (mn[j][i-1]<mn[j+(1<<(i-1))][i-1])
                    mnp[j][i]=mnp[j][i-1];
                else mnp[j][i]=mnp[j+(1<<(i-1))][i-1];
            }
        int x=0;
        for(int i=1;i<=n;i++)
        {
            if (i>(1<<(x+1))) x++;
            len[i]=x;
        }
    }
    
    void init_sum()
    {
        lft[0]=0;
        for(int i=1;i<=n;i++)
            lft[i]=lft[l[i]-1]+(ll)(i-l[i]+1)*a[i];
        rht[n+1]=0;
        for(int i=n;i>=1;i--)
            rht[i]=rht[r[i]+1]+(ll)(r[i]-i+1)*a[i];
    }
    
    int rmq(int l,int r)
    {
        int L=len[r-l+1];
        if (mn[l][L]<mn[r-(1<<L)+1][L]) return mnp[l][L];
        else return mnp[r-(1<<L)+1][L];
    }
    
    ll query(int l,int r,bool side)
    {
        int mp=rmq(l,r);
        ll sum=0;
        if (side)
        {
            sum+=lft[r]-lft[mp];
            sum+=(ll)(mp-l+1)*a[mp];
        }
        else
        {
            sum+=rht[l]-rht[mp];
            sum+=(ll)(r-mp+1)*a[mp];
        }
        return sum;
    }
    
    void extend(int x,bool type,bool side)
    {
        if (type) ans+=query(nowl,nowr,side);
        else ans-=query(nowl,nowr,side);
    }
    
    void Mo()
    {
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].id=i;
        }
        blocksiz=sqrt(n);
        sort(Q+1,Q+q+1,cmp);
        nowl=1,nowr=0;
        ans=0;
        for(int i=1;i<=q;i++)
        {
            while(Q[i].l<nowl) extend(--nowl,1,0);
            while(Q[i].r>nowr) extend(++nowr,1,1);
            while(Q[i].l>nowl) extend(nowl,0,0),nowl++;
            while(Q[i].r<nowr) extend(nowr,0,1),nowr--;
            Ans[Q[i].id]=ans;
        }
        for(int i=1;i<=q;i++)
            printf("%lld
    ",Ans[i]);
    }
    
    int main()
    {
        init();
        init_rmq();
        init_sum();
        Mo();
    
        return 0; 
    }
  • 相关阅读:
    HDU
    Hdu 5072 Coprime(容斥+同色三角形)
    HDU
    HTML常用基础标签
    简单session实现
    前端中的 IoC 理念
    怎样做页面界限
    Reset 对象属性
    SQL注入
    js:表单校验(获取元素、事件)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793417.html
Copyright © 2020-2023  润新知