[XJOI]栈

来源:互联网 发布:淘宝强光手电 编辑:程序博客网 时间:2024/05/16 13:39

题目大意

n个单调栈(单调递减)排成一排,一开始每个栈都是空的。
q个操作,每次会给下标在[l,r]内的栈都push一个x或者查询下标为k的栈里面所有数的和。

1n,q2×105,1x109


题目分析

考虑离线,把所有操作挂在对应端点上,然后从左向右扫描线。
以时间为下标建立线段树,考虑动态地维护当前扫到的单调栈在每一时间点的答案。
这个就十分套路了,和WC2013的楼房重建差不多。
先考虑如何查询,定义函数query([l,r],p)表示查询时间区间[l,r]组成的单调栈在最后加入p之后中所有数的和,查询时显然p=0调用一下就好了。
如果l=r,那么我们可以直接计算答案。
mid=l+r2rmx表示区间[mid+1,r]中所有数的最大值。
如果rmx>p,那么显然p不会对左半区间产生任何影响,而右边的最大值会加入到左边区间的单调栈中,答案等于query([l,mid],rmx)+query([mid+1,r],p)rmx
否则,右边的单调栈会直接被p全部弹出,答案等于query([l,mid],p)
如果我们一直这样递归下去,复杂度是没有保证的,怎么办呢?
fx表示x这个节点右半边区间最大值加入到左半边的单调栈之后,单调栈的所有数只和,即query(left.range,right.max)。先不考虑这个如何维护,反正我们每次修改之后线段树上每个位置的这个值都要修改过来。
假如我们的query操作中的区间已经是一个完整的线段树区间了,那么我们的rmx显然可以O(1)得到,query([l,mid],rmx)其实就是fx,也可以O(1)得到。
那么这个query的时间复杂度如何分析呢?
我们研究里面的嵌套函数调用了多少次。
首先,在我的区间还不是一个完整的线段树区间时,rmx需要通过调用区间最小值来得到,调用一次的复杂度是O(logq)的,而调用次数也是O(logq)的,因此这个复杂度是O(log2q)的。
其次,我们的查询操作会下放到O(logq)个完整的线段树区间,观察发现接下来我们往下走的过程已经不会增加新的分支,都是一条路走到底,因此每个区间最多会向下走O(logq)步,这个时间复杂度也是O(log2q)的。
至于fx怎么在每次修改之后重新得到?直接在update时调用一下query就好了。每次修改会调用O(logq)update,由于这时我的query都是在线段树上的完整区间上查,因此每次复杂度是O(logq)的。
于是总的时间复杂度就是O(qlog2q)的。这个套路真的很精彩。


代码实现

#include <algorithm>#include <iostream>#include <cstdio>#include <cctype>using namespace std;typedef long long LL;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}int buf[30];void write(LL x){    if (x<0) putchar('-'),x=-x;    for (;x;x/=10) buf[++buf[0]]=x%10;    if (!buf[0]) buf[++buf[0]]=0;    for (;buf[0];putchar('0'+buf[buf[0]--]));}const int Q=200050;const int M=Q<<1;const int S=Q<<2;struct query{    int opt,x,ti,val;    query (int opt_=0,int x_=0,int ti_=0,int val_=0){opt=opt_,x=x_,ti=ti_,val=val_;}    bool operator<(query const qry)const{return x<qry.x||x==qry.x&&opt<qry.opt;}}qy[M];inline int max(int x,int y){return x>y?x:y;}struct segment_tree{    int mx[S];    LL f[S];    int querymax(int x,int st,int en,int l,int r)    {        if (st==l&&en==r) return mx[x];        int mid=l+r>>1;        if (en<=mid) return querymax(x<<1,st,en,l,mid);        else if (mid+1<=st) return querymax(x<<1|1,st,en,mid+1,r);        else return max(querymax(x<<1,st,mid,l,mid),querymax(x<<1|1,mid+1,en,mid+1,r));    }    LL querysum(int x,int st,int en,int l,int r,int p)    {        if (l==r) return (mx[x]>p)*mx[x]+p;        int mid=l+r>>1;bool cvr=st==l&&en==r;        if (en<=mid) return querysum(x<<1,st,en,l,mid,p);        else if (mid+1<=st) return querysum(x<<1|1,st,en,mid+1,r,p);        else        {            int rmx=cvr?mx[x<<1|1]:querymax(x<<1|1,mid+1,en,mid+1,r);            if (rmx>p) return (cvr?f[x]:querysum(x<<1,st,mid,l,mid,rmx))+querysum(x<<1|1,mid+1,en,mid+1,r,p)-rmx;            else return querysum(x<<1,st,mid,l,mid,p);        }    }    void update(int x,int l,int r)    {        mx[x]=max(mx[x<<1],mx[x<<1|1]);        int mid=l+r>>1;        f[x]=querysum(x<<1,l,mid,l,mid,mx[x<<1|1]);    }    void modify(int x,int y,int l,int r,int val)    {        if (l==r)        {            mx[x]=val;            return;        }        int mid=l+r>>1;        if (y<=mid) modify(x<<1,y,l,mid,val);        else modify(x<<1|1,y,mid+1,r,val);        update(x,l,r);    }}t;int n,q,cnt,qid;LL ans[Q];void calc(){    sort(qy+1,qy+1+cnt);    for (int cur=1,ptr;cur<=cnt;)    {        for (ptr=cur;cur<=cnt&&qy[cur].x==qy[ptr].x&&!qy[cur].opt;++cur) t.modify(1,qy[cur].ti,1,q,qy[cur].val);        for (;cur<=cnt&&qy[cur].x==qy[ptr].x&&qy[cur].opt==1;++cur) ans[qy[cur].val]=t.querysum(1,1,qy[cur].ti,1,q,0);        for (;cur<=cnt&&qy[cur].x==qy[ptr].x;++cur) t.modify(1,qy[cur].ti,1,q,0);    }}int main(){    freopen("stack.in","r",stdin),freopen("stack.out","w",stdout);    n=read(),q=read();    for (int i=1;i<=q;++i)    {        int opt=read();        if (opt==1)        {            int l=read(),r=read(),x=read();            qy[++cnt]=query(0,l,i,x),qy[++cnt]=query(2,r,i,0);        }        else        {            int x=read();            qy[++cnt]=query(1,x,i,++qid);        }    }    calc();    for (int i=1;i<=qid;++i) write(ans[i]),putchar('\n');    fclose(stdin),fclose(stdout);    return 0;   }
原创粉丝点击