bzoj4826 [Hnoi2017]影魔(单调栈+主席树)

我们先单调栈求出每一个数左边/右边第一个大于他的数的位置L,R
那么以a[i]为最大数的贡献是:
左端点L[i],右端点R[i],贡献p1
左端点L[i]+1~i-1,右端点R[i],贡献p2
左端点L[i],右端点i+1~R[i]-1,贡献p2
我们如果把左右端点当做坐标的话,投射到二维平面上,那么我们每次求的就是一个矩形和。有一些单点加,某一维上的线段加。因此可以直接用主席树解决 O(nlogn) O ( n l o g n ) (建两棵主席树即可而不用树套树x)

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,A,B,a[N],L[N],R[N],qq[N],rt1[N],rt2[N],owo=0;
struct node{
    int lc,rc;ll x,tag;
}tr[N*20*5];
struct Icefox{
    int x,l,r,val;
    Icefox(){}
    Icefox(int _x,int _l,int _r,int _val){x=_x;l=_l;r=_r;val=_val;}
}b[N<<1],c[N];
inline bool cmp1(Icefox a,Icefox b){return a.xinline bool cmp2(Icefox a,Icefox b){return a.x>b.x;}
inline void ins(int &p,int l,int r,int x,int y,int val){
    tr[++owo]=tr[p];p=owo;tr[p].x+=(y-x+1)*val;
    if(l==x&&r==y){tr[p].tag+=val;return;}int mid=l+r>>1;
    if(y<=mid) ins(tr[p].lc,l,mid,x,y,val);
    else if(x>mid) ins(tr[p].rc,mid+1,r,x,y,val);
    else ins(tr[p].lc,l,mid,x,mid,val),ins(tr[p].rc,mid+1,r,mid+1,y,val);
}
inline ll ask(int p,int l,int r,int x,int y){
    if(!p) return 0;if(x==l&&r==y) return tr[p].x;
    int mid=l+r>>1;ll res=tr[p].tag*(y-x+1);
    if(y<=mid) return ask(tr[p].lc,l,mid,x,y)+res;
    if(x>mid) return ask(tr[p].rc,mid+1,r,x,y)+res;
    return ask(tr[p].lc,l,mid,x,mid)+ask(tr[p].rc,mid+1,r,mid+1,y)+res;
}
int main(){
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();m=read();A=read();B=read();
    for(int i=1;i<=n;++i) a[i]=read();
    int top=0,n1=0,n2=0;
    for(int i=1;i<=n;++i){
        while(top&&a[qq[top]]while(top) R[qq[top--]]=n+1;
    for(int i=1;i<=n;++i){
        if(L[i]&&R[i]<=n) b[++n1]=Icefox(R[i],L[i],L[i],A);
        if(L[i]+1<=i-1&&R[i]<=n) b[++n1]=Icefox(R[i],L[i]+1,i-1,B);
        if(L[i]&&i+1<=R[i]-1) c[++n2]=Icefox(L[i],i+1,R[i]-1,B);
    }sort(b+1,b+n1+1,cmp1);sort(c+1,c+n2+1,cmp2);int now=1;
    for(int i=1;i<=n;++i){
        rt1[i]=rt1[i-1];
        while(now<=n1&&b[now].x==i) ins(rt1[i],1,n,b[now].l,b[now].r,b[now].val),++now;
    }now=1;
    for(int i=n;i>=1;--i){
        rt2[i]=rt2[i+1];
        while(now<=n2&&c[now].x==i) ins(rt2[i],1,n,c[now].l,c[now].r,c[now].val),++now;
    }while(m--){
        int x=read(),y=read();ll ans=A*(y-x);
        ans+=ask(rt1[y],1,n,x,y);
        ans-=ask(rt1[x-1],1,n,x,y);
        ans+=ask(rt2[x],1,n,x,y);
        ans-=ask(rt2[y+1],1,n,x,y);
        printf("%lld\n",ans);
    }return 0;
}

你可能感兴趣的:(bzoj,主席树)