【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\n",Ans[i]);
}

int main()
{
    init();
    init_rmq();
    init_sum();
    Mo();

    return 0; 
}
posted @ 2018-05-01 19:35  Maxwei_wzj  阅读(84)  评论(0编辑  收藏  举报