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

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

猜你喜欢

转载自blog.csdn.net/maxwei_wzj/article/details/80158819