P6109 [Ynoi2009] rprmq1
Description
有一个 \(n \times n\) 的矩阵 \(a\),初始全是 \(0\),有 \(m\) 次修改操作和 \(q\) 次查询操作,先进行所有修改操作,然后进行所有查询操作。
一次修改操作会给出 \(l_1,l_2,r_1,r_2,x\),代表把所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素加上一个值 \(x\)。
一次查询操作会给出 \(l_1,l_2,r_1,r_2\),代表查询所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素的最大值。
\(1\leq n,m\leq 5\times 10^4\),\(1\leq q \leq 5\times 10^5\)。
Solution
如果所有询问的 \(l_1\) 都是同一个值,应该怎么做?
考虑扫描线,将修改和查询的矩形拆为与 \(y\) 轴平行的线段。扫到一个查询时,其答案就对应着一段区间的历史最大值。
对于一般的情况,又该怎么做?
考虑猫树分治,将 \(x\) 轴与询问放在一起分治。
分治到 \((l,r)\) 时,我们只处理被 \(mid\) 划分为两段的询问,其余询问扔给后面处理。
此时,我们在 $mid $ 左右分别做一遍扫描线,这样就转化为所有询问的 左/右 端点都相同的情况。
每个询问至多递归 \(O(\log n)\) 层就会被某一个 \(mid\) 划分为两部分,而且得出答案的复杂度也为 $O(\log n) $,所以处理询问的复杂度为 \(O(Q\log n)\)。
但是修改操作不一样。即使一个修改操作在某一层被 $mid $ 划分为两部分,仍然需要递归进两边处理,这样一个修改操作至多会进行 \(O(n)\) 次,时间复杂度为 \(O(nm\log n)\),无法接受。
注意到我们把分治树刻画出来,类似于线段树,一个修改操作最多会影响 \(O(\log n)\) 个节点。
如果一个修改操作完全覆盖了一个分治树上的节点,那么我们就将其作为下一层的初值处理,不再向下递归。那么处理一个修改操作的复杂度降为 \(O(\log^2 n)\),加起来就是 \(O(m\log^2 n)\)。
在实现中,我们需要将操作撤销,并将历史最大值恢复到操作之前。这对应着把历史最大值全部置为当前最大值。
我们在线段树节点上增加一个延时标记 tag
,表示这个节点的数据已经恢复完毕,其子树还没有恢复。
当一个节点被打上 tag
这个标记时,需要先下放其他标记,然后把这个节点的历史最大值赋值为当前最大值。
总时间复杂度为 \(O(m\log^2 n+Q \log n)\)。
int n,Q1,Q2;struct Rect{int lx,ly,rx,ry,v;
};
vector<Rect> q1,q2;struct Line{int x,l,r,v,id;
};ll ans[M];bool CmpR(Line x,Line y){if(x.x!=y.x) return x.x<y.x;else if(x.id!=y.id) return x.id<y.id;else return x.v<y.v;
}bool CmpL(Line x,Line y){if(x.x!=y.x) return x.x>y.x;else if(x.id!=y.id) return x.id<y.id;else return x.v<y.v;
}struct SegTree{struct SegNode{ll val,mxv,add,mxd;bool tag;}tr[N<<3];void WorkAdd(int p,ll mxd,ll add){Ckmax(tr[p].mxv,tr[p].val+mxd);tr[p].val+=add;Ckmax(tr[p].mxd,tr[p].add+mxd);tr[p].add+=add;}void Roll(int p){WorkAdd(p<<1,tr[p].mxd,tr[p].add);WorkAdd(p<<1|1,tr[p].mxd,tr[p].add);tr[p].add=tr[p].mxd=0;tr[p].mxv=tr[p].val; tr[p].tag=1;}void Spread(int p){if(tr[p].tag) Roll(p<<1),Roll(p<<1|1),tr[p].tag=0;WorkAdd(p<<1,tr[p].mxd,tr[p].add);WorkAdd(p<<1|1,tr[p].mxd,tr[p].add);tr[p].mxd=tr[p].add=0;}void Pushup(int p){tr[p].val=max(tr[p<<1].val,tr[p<<1|1].val);tr[p].mxv=max(tr[p<<1].mxv,tr[p<<1|1].mxv); }void Update(int p,int l,int r,int L,int R,int v){if(L<=l&&r<=R) return WorkAdd(p,v,v);int mid=(l+r)>>1; Spread(p);if(L<=mid) Update(p<<1,l,mid,L,R,v);if(R>mid) Update(p<<1|1,mid+1,r,L,R,v);Pushup(p);}ll Ask(int p,int l,int r,int L,int R){if(L<=l&&r<=R) return tr[p].mxv;int mid=(l+r)>>1; ll res=0; Spread(p);if(L<=mid) Ckmax(res,Ask(p<<1,l,mid,L,R));if(R>mid) Ckmax(res,Ask(p<<1|1,mid+1,r,L,R));return res;}
}Seg;struct Start{int l,r,v;
};void Solve(int l,int r,vector<Rect> U,vector<Rect> Q){if(Q.empty()) return;if(l==r||U.empty()){for(Rect i:Q){ll res=Seg.Ask(1,1,n,i.ly,i.ry);Ckmax(ans[i.v],res);}return;}vector<Rect> UL,UR,QL,QR;vector<Start> CL,CR;vector<Line> SL,SR;UL.reserve(U.size()),UR.reserve(U.size());CL.reserve(U.size()),CR.reserve(U.size());QL.reserve(Q.size()),QR.reserve(Q.size());int mid=(l+r)>>1;for(Rect i:Q){if(i.rx<=mid) QL.push_back(i);else if(i.lx>=mid+1) QR.push_back(i);else{SL.push_back({i.lx,i.ly,i.ry,0,i.v});SR.push_back({i.rx,i.ly,i.ry,0,i.v});}}for(Rect i:U){if(i.rx<=mid){if(i.rx==mid&&i.lx==l) CL.push_back({i.ly,i.ry,i.v});else UL.push_back(i);SL.push_back(Line{i.rx,i.ly,i.ry,i.v,0});SL.push_back(Line{i.lx-1,i.ly,i.ry,-i.v,0});}else if(i.lx>mid){if(i.lx==mid+1&&i.rx==r) CR.push_back({i.ly,i.ry,i.v});else UR.push_back(i);SR.push_back(Line{i.lx,i.ly,i.ry,i.v,0});SR.push_back(Line{i.rx+1,i.ly,i.ry,-i.v,0});}else{if(i.lx==l) CL.push_back({i.ly,i.ry,i.v});else UL.push_back(Rect{i.lx,i.ly,mid,i.ry,i.v});if(i.rx==r) CR.push_back({i.ly,i.ry,i.v});else UR.push_back(Rect{mid+1,i.ly,i.rx,i.ry,i.v});SL.push_back({mid,i.ly,i.ry,i.v,0});SL.push_back({i.lx-1,i.ly,i.ry,-i.v,0});SR.push_back({mid+1,i.ly,i.ry,i.v,0});SR.push_back({i.rx+1,i.ly,i.ry,-i.v,0});}}sort(SL.begin(),SL.end(),CmpL);sort(SR.begin(),SR.end(),CmpR);for(Line i:SL){if(!i.id) Seg.Update(1,1,n,i.l,i.r,i.v);else Ckmax(ans[i.id],Seg.Ask(1,1,n,i.l,i.r));}Seg.Roll(1); for(Start i:CL) Seg.Update(1,1,n,i.l,i.r,i.v);Solve(l,mid,UL,QL);for(Start i:CL) Seg.Update(1,1,n,i.l,i.r,-i.v);Seg.Roll(1);for(Line i:SR){if(!i.id) Seg.Update(1,1,n,i.l,i.r,i.v);else Ckmax(ans[i.id],Seg.Ask(1,1,n,i.l,i.r));}Seg.Roll(1);for(Start i:CR) Seg.Update(1,1,n,i.l,i.r,i.v);Solve(mid+1,r,UR,QR);for(Start i:CR) Seg.Update(1,1,n,i.l,i.r,-i.v);Seg.Roll(1);
}signed main(){read(n),read(Q1),read(Q2);q1.reserve(Q1),q2.reserve(Q2);for(int i=1;i<=Q1;i++){Rect res;read(res.lx),read(res.ly);read(res.rx),read(res.ry);read(res.v);if(res.lx==1&&res.rx==n) Seg.Update(1,1,n,res.ly,res.ry,res.v);else q1.push_back(res);}for(int i=1;i<=Q2;i++){Rect res;read(res.lx),read(res.ly);read(res.rx),read(res.ry);res.v=i;q2.push_back(res);}Solve(1,n,q1,q2);for(int i=1;i<=Q2;i++) printf("%lld\n",ans[i]);return 0;