BZOJ4262Sum 单调栈+线段树
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4262Sum 单调栈+线段树相关的知识,希望对你有一定的参考价值。
【BZOJ4262】Sum
Description
Input
第一行一个数 t,表示询问组数。
第一行一个数 t,表示询问组数。
接下来 t 行,每行四个数 l_1, r_1, l_2, r_2。
Output
一共 t 行,每行一个数 Sum。
Sample Input
4
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1
Sample Output
9322587654
9025304064
1065645568
0
9025304064
1065645568
0
HINT
1<=t<=40000,1<=L1<R1<=10^5,1<=L2<=R2<=10^5
题解:我们分开考虑max和pre的情况。我们将max(i...j)视为二维平面上点(i,j)的权值,处理出每个数左边第一个比它大的数,然后这个数的贡献区间可以就看成一个矩形(或三角形),而询问就变成了求平面上一个矩形区域的权值和。可以用线段树来搞。
不过线段树维护历史总和还真是不容易,打标记的部分还是好好说说吧。
维护三个值:v代表当前的区间和,s代表历史的v之和,l代表区间长度。
维护四个标记:a,b,c,d,代表标记生效后,v=a*v+b*l,s=s+c*v+d*l。
关键在于标记如何合并。假如我们要将x和y的标记合并成z。
a:显然z.a=x.a*y.a即可。
b:先要*=y.a,还要+=y.b。
c:+=x.a*y.c。
d:先要+=y.d,还要+=x.b*y.c。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define lson x<<1 #define rson x<<1|1 using namespace std; const int maxn=100010; typedef long long ll; struct Tag { ll a,b,c,d; Tag () {a=1,b=c=d=0;} Tag (ll A,ll B,ll C,ll D) {a=A,b=B,c=C,d=D;} Tag operator + (const Tag &x) const {return Tag(a*x.a,b*x.a+x.b,a*x.c+c,d+b*x.c+x.d);} }; struct node { ll v,s,l; Tag t; node () {v=s=l=0,t=Tag();} node (ll a,ll b,ll c,Tag d) {v=a,s=b,l=c,t=d;} inline void add(Tag x) { s=s+v*x.c+l*x.d,v=v*x.a+l*x.b,t=t+x; } node operator + (const node &a) const { return node(v+a.v,s+a.s,l+a.l,Tag()); } }s[maxn<<2]; int m,n,top; ll ans[maxn],v[maxn]; int st[maxn],pre[maxn]; struct QUERY { int x,l,r,org,k; }q[maxn]; bool cmp(const QUERY &a,const QUERY &b) { return a.x<b.x; } inline void pushdown(int x) { if(s[x].t.a!=1||s[x].t.b||s[x].t.c||s[x].t.d) s[lson].add(s[x].t),s[rson].add(s[x].t),s[x].t=Tag(); } void build(int l,int r,int x) { if(l==r) { s[x]=node(),s[x].l=1; return ; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); s[x]=s[lson]+s[rson]; } void updata(int l,int r,int x,int a,int b,Tag t) { if(a>b) return ; if(a<=l&&r<=b) { s[x].add(t); return ; } pushdown(x); int mid=(l+r)>>1; if(a<=mid) updata(l,mid,lson,a,b,t); if(b>mid) updata(mid+1,r,rson,a,b,t); s[x]=s[lson]+s[rson]; } node query(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) return s[x]; pushdown(x); int mid=(l+r)>>1; if(b<=mid) return query(l,mid,lson,a,b); if(a>mid) return query(mid+1,r,rson,a,b); return query(l,mid,lson,a,b)+query(mid+1,r,rson,a,b); } void work(ll flag) { int i,j; build(1,n,1); for(j=1;j<=2*m&&!q[j].x;j++); for(i=1;i<=n;i++) { updata(1,n,1,pre[i],i,Tag(0,v[i],0,0)),s[1].add(Tag(1,0,1,0)); for(;j<=2*m&&q[j].x==i;j++) ans[q[j].org]+=flag*q[j].k*query(1,n,1,q[j].l,q[j].r).s; } } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { m=rd(); int i; ll t1=1,t2=1; for(i=1;i<=m;i++) q[i].l=q[i+m].l=rd(),q[i].r=q[i+m].r=rd(),q[i].x=rd()-1,q[i+m].x=rd(), n=max(n,q[i+m].x),q[i].k=-1,q[i+m].k=1,q[i].org=q[i+m].org=i; for(i=1;i<=n;i++) t1=t1*1023%1000000000,t2=t2*1025%1000000000,v[i]=t1^t2; sort(q+1,q+2*m+1,cmp); for(i=1;i<=n;i++) { while(top&&v[st[top]]>=v[i]) top--; pre[i]=st[top]+1,st[++top]=i; } work(-1); for(top=0,i=1;i<=n;i++) { while(top&&v[st[top]]<=v[i]) top--; pre[i]=st[top]+1,st[++top]=i; } work(1); for(i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }
以上是关于BZOJ4262Sum 单调栈+线段树的主要内容,如果未能解决你的问题,请参考以下文章
CF526F Pudding Monsters 线段树+单调栈