[Ynoi2009] rprmq1

[Ynoi2009] rprmq1

给你一个初始为 0 的方阵,要先进行若干矩形加,然后再询问若干个矩形 \(\max\)

又是神题一道……

对于修改,可以将一维差分,一维当时间维,两维用线段树处理,这便是二维数点扫描线的套路

但是询问却差分不了,怎么样才能也让询问支持扫描线呢?

发现修改差分后,扫描线扫过一个查询矩阵,那么矩阵一行的值会不断变化,答案便是时间在 \([l_1,r_1]\) 之间的所有最大值的最大值,也就是历史最大值

那么通过分治等技巧,我们就可以通过扫描时间区间的翻转腾挪了来求得所有的最大值(莫队大概就是这样的一种思想)

但是历史最大值这玩意的确不好维护删除,但是你可以快速清空,具体做法就是在线段树上打清空标记

由于我们需要求的是一个时间前缀(因为差分),而且还要适时开始记录历史最大值,这时候就有一个神奇的分治做法

考虑猫树思想(似乎叫猫树分治或二区间合并),每个询问挂到线段树的一个节点上,被 \(Mid\) 劈成两个询问区间

考虑扫描线扫过当前区间 \([L,R]\) 的过程

扫描线先不记录历史最大值扫到 \(Mid\) ,然后记录历史最大值处理右半部分的询问,递归 \([Mid+1,R]\) ,然后扫描线再鬼畜回退到 \(Mid+1\) ,记录历史最大值推回到 \(L\),递归 \([L,Mid]\)

形象理解就是一个维护前缀的扫描线在线段树上鬼畜的推来退去

注意一下同一个时间点的询问需要排个序,避免提前更新历史最大值

又被卡常了……把线段树从结构体换成数组就过了

#include <cstdio>
#include <vector>
#include <algorithm>
#define gec() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
char stk[100],*tp=stk;
typedef long long ll;
int read(){
	char c=gec();int x=0;
	while(c<48||c>57) c=gec();
	do x=(x<<1)+(x<<3)+(c^48),c=gec();
	while(c>=48&&c<=57);
	return x;
}
void write(ll x){
	do *++tp=(x%10)^48,x/=10;while(x);
	while(tp!=stk) putchar(*tp--);
	putchar('\n');
}
void chmx(ll &a,ll b){if(a<b) a=b;}
const int N=50003;
const int M=500003;
bool rst[N<<2];
ll tg[N<<2],htg[N<<2],mx[N<<2],hmx[N<<2];
int n,m,q;
void proc(int p,ll stg,ll shtg){
	chmx(hmx[p],mx[p]+shtg);
	chmx(htg[p],tg[p]+shtg);
	mx[p]+=stg;tg[p]+=stg;
}
void rset(int p){
	if(p<=(n<<1)){
		proc(p<<1,tg[p],htg[p]);
		proc(p<<1|1,tg[p],htg[p]);
		tg[p]=htg[p]=0;
	}
	rst[p]=1;hmx[p]=mx[p];
}
void pushdown(int p){
	if(rst[p]) rset(p<<1),rset(p<<1|1),rst[p]=0;
   	proc(p<<1,tg[p],htg[p]);
   	proc(p<<1|1,tg[p],htg[p]);
   	tg[p]=htg[p]=0;
}
void modify(int p,int L,int R,ll V,int l,int r){
	if(L<=l&&r<=R) return proc(p,V,V);
	pushdown(p);
	int mid=(l+r)>>1;
	if(L<=mid) modify(p<<1,L,R,V,l,mid);
	if(R>mid) modify(p<<1|1,L,R,V,mid+1,r);
	mx[p]=max(mx[p<<1],mx[p<<1|1]);
	hmx[p]=max(hmx[p<<1],hmx[p<<1|1]);
}
ll query(int p,int L,int R,int l,int r){
	if(L<=l&&r<=R) return hmx[p];
	pushdown(p);
	ll res=0;int mid=(l+r)>>1;
	if(L<=mid) chmx(res,query(p<<1,L,R,l,mid));
	if(R>mid) chmx(res,query(p<<1|1,L,R,mid+1,r));
	return res;
}
struct option{int l,r;ll v;};
bool operator<(const option x,const option y){return x.v<y.v;}
ll res[M];
int Ls[M],Rs[M],Lq[M],Rq[M];
int hd[N<<2],nxt[M];
int Fhd[N],Fnxt[M];
vector<option> Os[N];
void hang(int p,int L,int R,int V,int l,int r){
	int mid=(l+r)>>1;
	if(L<=mid+1&&mid<=R) {nxt[V]=hd[p];hd[p]=V;return;}
	if(R<=mid) hang(p<<1,L,R,V,l,mid);
	if(L>mid) hang(p<<1|1,L,R,V,mid+1,r);
}
void contrib(int p,int v){
	int sz=Os[p].size();
	if(v>0) for(int i=0;i<sz;++i) modify(1,Os[p][i].l,Os[p][i].r,v*Os[p][i].v,1,n);
	else for(int i=sz-1;~i;--i) modify(1,Os[p][i].l,Os[p][i].r,v*Os[p][i].v,1,n);
}
void solve(int p,int l,int r){
	int mid=(l+r)>>1;
	for(int i=l;i<=mid;++i) contrib(i,1);
	for(int i=mid+1;i<=r;++i) Fhd[i]=0;
	for(int x=hd[p];x;x=nxt[x]) Fnxt[x]=Fhd[Rs[x]],Fhd[Rs[x]]=x;
	for(int i=mid+1;i<=r;++i){
		contrib(i,1);
		if(i==mid+1) rset(1);
		for(int x=Fhd[i];x;x=Fnxt[x])
			chmx(res[x],query(1,Lq[x],Rq[x],1,n));
	}
	for(int i=r;i>mid;--i) contrib(i,-1);
	if(l^r) solve(p<<1|1,mid+1,r);
	for(int i=l;i<=mid;++i) Fhd[i]=0;
	for(int x=hd[p];x;x=nxt[x]) Fnxt[x]=Fhd[Ls[x]],Fhd[Ls[x]]=x;
	rset(1);
	for(int i=mid;i>=l;--i){
		for(int x=Fhd[i];x;x=Fnxt[x])
			chmx(res[x],query(1,Lq[x],Rq[x],1,n));
		contrib(i,-1);
	}
	if(l^r) solve(p<<1,l,mid);
}
int main(){
	n=read();m=read();q=read();
	for(int i=1;i<=m;++i){
		int l1=read(),l2=read(),r1=read(),r2=read(),x=read();
		Os[l1].emplace_back((option){l2,r2,x});
		Os[r1+1].emplace_back((option){l2,r2,-x});
	}
	for(int i=1;i<=n;++i) sort(Os[i].begin(),Os[i].end());
	for(int i=1;i<=q;++i){
		Ls[i]=read();Lq[i]=read();Rs[i]=read();Rq[i]=read();
		hang(1,Ls[i],Rs[i],i,1,n);
	}
	solve(1,1,n);
	for(int i=1;i<=q;++i) write(res[i]);
	return 0;
}
posted @ 2022-03-04 22:20  yyyyxh  阅读(42)  评论(0编辑  收藏  举报