bzoj 4540: [Hnoi2016]序列 莫队算法+rmq

来源:互联网 发布:怎么在淘宝买种子 编辑:程序博客网 时间:2024/06/13 21:55

题意

给出一个长度为n的序列和q个询问,没个询问l r表示求[l,r]中每一个子序列的最小值的和。
n,q<=100000

分析

正解是非常玄学的线段树,反正我是不会了,不过据说跑的还没有莫队快……

首先很容易想到离线做,但是莫队要怎么更新呢?总不能O(n)扫一遍吧。
我们假设现在莫队处理到的区间为[l,r],那么我要将其变为[l,r-1],那么就是要减去所有区间[i,r](l< =i<=r)的最小值。那么我们设ls[i]表示往左第一个不大于i的数的下标,那么减去的贡献就相当于a[r](rls[r])+a[ls[r]](ls[r]ls[ls[r]])如此类推,那么就可以维护两个链表前缀和,一个向后,一个向前,分别处理左、右指针的更新。然后再维护一个rmq,就可以O(1)转移啦。

一遍过真爽啊!!!一开始T是因为每次rmq调用了cmath库中的log函数,后面把log预处理了一下就跑的飞快了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>#define N 100005#define ll long longusing namespace std;int n,m,pos[N],f[N][20],a[N],ls[N],nx[N],s[N],logn[N];ll suml[N],sumr[N],ans;struct query{int l,r,id;ll ans;}q[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void getrmp(){    for (int i=1;i<=n;i++)        f[i][0]=i;    for (int i=1;i<=16;i++)        for (int j=1;j<=n;j++)            if (j+(1<<i)-1<=n)             {                if (a[f[j][i-1]]<a[f[j+(1<<(i-1))][i-1]]) f[j][i]=f[j][i-1];                else f[j][i]=f[j+(1<<(i-1))][i-1];            }            else break;}void prework(){    for (int i=1;i<=n;i++)        logn[i]=log(i)/log(2);    int head=1,tail=0;    for (int i=1;i<=n;i++)    {        while (head<=tail&&a[s[tail]]>a[i]) tail--;        if (head<=tail) ls[i]=s[tail];        s[++tail]=i;        suml[i]=suml[ls[i]]+(ll)a[i]*(i-ls[i]);    }    head=1,tail=0;    for (int i=n;i>=1;i--)    {        while (head<=tail&&a[s[tail]]>a[i]) tail--;        if (head<=tail) nx[i]=s[tail];        s[++tail]=i;        sumr[i]=sumr[nx[i]]+(ll)a[i]*(nx[i]-i);    }}bool cmp(query a,query b){    return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;}bool cmpid(query a,query b){    return a.id<b.id;}int getmin(int l,int r){    int w=logn[r-l+1];    if (a[f[l][w]]<a[f[r-(1<<w)+1][w]]) return f[l][w];    else return f[r-(1<<w)+1][w];}void updatal(int l,int r,int x){    int w=getmin(l,r);    ans+=(ll)(sumr[l]-sumr[w]+(ll)a[w]*(r-w+1))*x;}void updatar(int l,int r,int x){    int w=getmin(l,r);    ans+=(ll)(suml[r]-suml[w]+(ll)a[w]*(w-l+1))*x;}void solve(){    int l=1,r=1;    ans=a[1];    for (int i=1;i<=m;i++)    {        for (;r<q[i].r;r++)            updatar(l,r+1,1);        for (;l>q[i].l;l--)            updatal(l-1,r,1);        for (;r>q[i].r;r--)            updatar(l,r,-1);        for (;l<q[i].l;l++)            updatal(l,r,-1);        q[i].ans=ans;    }}int main(){    n=read();m=read();    int block=sqrt(n);    for (int i=1;i<=n;i++)    {        a[i]=read();        pos[i]=(i+block-1)/block;    }    getrmp();    prework();    for (int i=1;i<=m;i++)    {        q[i].l=read();q[i].r=read();        q[i].id=i;    }    sort(q+1,q+m+1,cmp);    solve();    sort(q+1,q+m+1,cmpid);    for (int i=1;i<=m;i++)        printf("%lld\n",q[i].ans);    return 0;}
0 0
原创粉丝点击