P6109 [Ynoi2009] rprmq1 做题记录

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;

posted @ 2025-03-25 20:43  XP3301_Pipi  阅读(34)  评论(0)    收藏  举报
Title