BZOJ 4826 [Hnoi2017]影魔 ——扫描线 单调栈

首先用单调栈和扫描线处理出每一个数左面最近的比他大的数在$l[i]$,右面最近的比他大的数$r[i]$。

然后就可以考虑每种贡献是在什么时候产生的。

1、$(l[i],r[i])$产生$p1$的贡献

2、$([l[i]]-[i-1],i)$产生$p2$的贡献

3、$(i,[i+1]-[r[i]])$产生$p2$的贡献。

然后发现在笛卡尔坐标系中是一些线段和点,然后平行与扫描线的比较好解决。

但是垂直的就比较麻烦了。

然后有人用六棵主席树做过去了,也有人用四棵。

其实只要线段树分两次扫一遍就好了。

#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define maxn 200005
 
struct Options{
    int opt; //1 add 0 query
    int x,y,z,d,id;
    Options() {}
    Options(int _opt,int _x,int _y,int _z,int _d)
    {opt=_opt;x=_x;y=_y;z=_z;d=_d;}
    void print()
    {
        printf("Opt %d ( %d , %d , %d ) ID %d D: %d\n",opt,x,y,z,id,d);
    }
}q[maxn<<3];
 
int n,m,p1,p2,a[maxn],cnt=0,sta[maxn],top,l[maxn],r[maxn],_l[maxn],_r[maxn];
ll ans[maxn],sum[maxn<<3],mark[maxn<<3];
 
bool cmp1(Options a,Options b)
{return a.x==b.x?a.opt>b.opt:a.x<b.x;}
bool cmp2(Options a,Options b)
{return a.y==b.y?a.opt>b.opt:a.y<b.y;}
 
void update(int o)
{sum[o]=sum[o<<1]+sum[o<<1|1];}
 
void pushdown(int o,int l,int r)
{
    if (mark[o]!=0)
    {
        int mid=l+r>>1;
        sum[o<<1]+=(mid-l+1)*mark[o];
        sum[o<<1|1]+=(r-mid)*mark[o];
        mark[o<<1]+=mark[o];
        mark[o<<1|1]+=mark[o];
        mark[o]=0;
    }
}
 
void modify(int o,int l,int r,int L,int R,int f)
{
    if (L<=l&&r<=R)
    {
        mark[o]+=f;
        sum[o]+=(r-l+1)*f;
        return;
    }
    int mid=l+r>>1;pushdown(o,l,r);
    if (L<=mid) modify(o<<1,l,mid,L,R,f);
    if (R>mid) modify(o<<1|1,mid+1,r,L,R,f);
    update(o);
}
 
ll query(int o,int l,int r,int L,int R)
{
    if (L<=l&&r<=R) return sum[o];
    int mid=l+r>>1;pushdown(o,l,r);
    if (R<=mid) return query(o<<1,l,mid,L,R);
    if (L>mid) return query(o<<1|1,mid+1,r,L,R);
    return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}
 
int main()
{
    scanf("%d%d%d%d",&n,&m,&p1,&p2);
    F(i,1,n) scanf("%d",&a[i]);
    F(i,1,m)
    {
        int x,y; scanf("%d%d",&_l[i],&_r[i]); x=_l[i];y=_r[i];
        q[++cnt].id=i;q[cnt].d=1; q[cnt].x=y+1;q[cnt].y=y+1;q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=1; q[cnt].x=x;  q[cnt].y=x;  q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=-1;q[cnt].x=x;  q[cnt].y=y+1;q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=-1;q[cnt].x=y+1;q[cnt].y=x;  q[cnt].opt=0;
    }
    F(i,1,n)
    {
        while(top&&a[sta[top]]<a[i]) top--;
        l[i]=sta[top];
        sta[++top]=i;
    }
    sta[top=0]=n+1;
    D(i,n,1)
    {
        while(top&&a[sta[top]]<a[i]) top--;
        r[i]=sta[top];
        sta[++top]=i;
    }
    F(i,1,n-1)
    {
        q[++cnt].opt=1;q[cnt].x=i+1;q[cnt].y=i+2;q[cnt].z=i+2;q[cnt].d=p1;
    }
    F(i,1,n)
    {
        q[++cnt]=Options(1,l[i]+1,r[i]+1,r[i]+1,p1);
        if (r[i]-1>=i+1) q[++cnt]=Options(1,l[i]+1,i+2,r[i],p2);
    }
    sort(q+1,q+cnt+1,cmp1);
    F(i,1,cnt)
    {
        switch(q[i].opt)
        {
            case 0:ans[q[i].id]+=q[i].d*query(1,1,n+2,1,q[i].y);break;
            case 1:modify(1,1,n+2,q[i].y,q[i].z,q[i].d);break;
        }
    }
    memset(sum,0,sizeof sum); memset(mark,0,sizeof mark);cnt=0;
    F(i,1,m)
    {
        int x,y;x=_l[i];y=_r[i];
        q[++cnt].id=i;q[cnt].d=1; q[cnt].x=y+1;q[cnt].y=y+1;q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=1; q[cnt].x=x;  q[cnt].y=x;  q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=-1;q[cnt].x=x;  q[cnt].y=y+1;q[cnt].opt=0;
        q[++cnt].id=i;q[cnt].d=-1;q[cnt].x=y+1;q[cnt].y=x;  q[cnt].opt=0;
    }
    F(i,1,n) if (l[i]+1<=i-1) q[++cnt]=Options(1,l[i]+2,r[i]+1,i,p2);
    sort(q+1,q+cnt+1,cmp2);
    F(i,1,cnt)
    {
        switch(q[i].opt)
        {
            case 0:ans[q[i].id]+=q[i].d*query(1,1,n+2,1,q[i].x);break;
            case 1:modify(1,1,n+2,q[i].x,q[i].z,q[i].d);break;
        }
    }
    F(i,1,m) printf("%lld\n",ans[i]);
}

  

posted @ 2017-05-05 16:02  SfailSth  阅读(186)  评论(0编辑  收藏  举报