WrongAnswer_90

一言(ヒトコト)

P6109 [Ynoi2009] rprmq1

My Blogs

P6109 [Ynoi2009] rprmq1

毒瘤 Ynoi。

对于加的矩形,显然是差分的拆成 \((l_1,l_2,r_2,+v)\)\((r_1,l_2,r_2,-v)\) 两个线段。如果是查询的是和那是好做的,线段树历史和板子。

但是要查的是最大值,矩形查最大值因为没有可减性,不能差分,非常的困难。考虑一个弱化问题:如果是查 \(3-\text{side}\) 矩形的最大值,这样是好做的,扫描 \(x\) 轴,\(y\) 轴维护一棵线段树,支持区间加和区间查询历史最大值即可。

这启发我们把 \(4-\text{side}\) 矩形转化。考虑对于每个询问找到一条竖线,把询问从中间劈开,变成两个 \(3-{\text{side}}\) 矩形。这样仍然保留线段树维护 \(y\) 轴,从中间向左做一遍,向右再做一遍就能得到答案。

处理这种问题,最常用的就是猫树分治:建一棵线段树,对于每个询问在 \(x\) 坐标上找到它跨过中点的最浅的节点把询问挂在这个节点上。然后 \(y\) 坐标上再用线段树区间加,查询历史最大值维护。

朴素分治

设当前处理的区间是 \(x:[l,r]\)(横坐标),需要知道的是两棵下标是 \(y\) 坐标的线段树:

  1. 维护横坐标在 \([1,l-1]\) 的所有线段的和与历史最大值。

  2. 维护横坐标在 \([r+1,n]\) 的所有线段的和与历史最大值。

这样处理该区间的时候,先用第一棵线段树从 \(l\)\(r\) 扫,遇到挂在该节点的询问的右端点时就更新答案。假设询问的 \(x\) 区间是 \([l_i,r_i]\),这部分处理的是 \(x\in[mid+1,r_i]\) 的所有位置对询问的贡献。

但是这样会有问题:希望查询的是 \(x\in[mid+1,r]\) 的历史最大值,但是这样会统计上 \([1,mid]\) 的贡献。

解决方案是在 \(mid+1\) 处全局加 \(\text{INF}\),查询的时候的真实结果是 \(\text{ask}-sum\),这样 \([1,mid]\) 的所有历史最大值就一定劣于 \([mid+1,r]\)

然后再用第二棵线段树从 \(r\)\(l\) 扫一遍,处理 \(x\in[l_i,mid]\) 对询问的贡献。做和上面一样的过程即可。

接下来是向左和向右递归。向左递归时,右侧线段树应当加入 \(x\in[mid+1,r]\) 的所有线段。

这时就会发现需要做一个撤销或者是可持久化的过程,空间有点不优秀或者是常数比较大还不好写。

进一步优化

考虑把同层分治放到一起处理。如:

1.png

假设处理的是灰色区间,这样需要扫 \(3\) 遍,第一次是第一层,第二次处理第二层,以此类推。

处理一层的时候和分治的时候差不多,从 \(1\) 扫到 \(n\),遇到线段就加入,遇到黑区间的左端点就全局 \(+\text{INF}\)(查询的时候再减回来)。这样就不需要做线段树的可持久化或者是撤销。需要扫 \(2\log n\) 次,每次的加矩形的复杂度是 \(\mathcal O(m\log n)\),每个询问只会在某一层造成复杂度,所以总复杂度是 \(\mathcal O(m\log^2 n+q\log n)\)

一些细节

可以先把 \(n\) 设成 \(65536\) 方便进行中点分治。把平面加矩形拆成线段的时候,需要注意不能用在 \(l\) 处加,\(r+1\) 处减,共用一个 vector。因为有全局加 \(\text{INF}\) 的操作,这样处理会有边界的问题,比如一个矩形的右端点是白区间的右端点,就会先加 \(\text{INF}\) 再减去该矩形右侧的线段,导致出错。

正确的姿势是开两个 vector,一个在 \(l_1\) 处存 \((l_2,r_2,v)\),一个在 \(r_1\) 处存 \((l_2,r_2,v)\),这样从左向右扫的时候,在一个点上的操作就是:

  1. 处理第一个 vector 中的加。

  2. 处理询问。

  3. 处理第二个 vector 中的减。

这样可以规避许多问题。需要开 ll,但是不要全开,容易 TLE。

	int n,m,q;
	ll ans[500010];
	struct Node{int l1,r1,l2,r2,id;}b[500010];
	vector<tup> ve[65537],ev[65537],qu[65537];
	namespace Segment
	{
		#define ls(p) (t[p].l+t[p].r)
		#define rs(p) (ls(p)^1)
		struct{int l,r;ll v,s,tg2,tg3;}t[132000];
		inline void update(int p){t[p].v=max(t[ls(p)].v,t[rs(p)].v),t[p].s=max(t[ls(p)].s,t[rs(p)].s);}
		inline void down(int p,ll x)
		{
			t[p].tg2+=x,Mmax(t[p].tg3,t[p].tg2);
			t[p].v+=x,Mmax(t[p].s,t[p].v);
		}
		inline void down2(int p,ll x){Mmax(t[p].tg3,x+t[p].tg2),Mmax(t[p].s,t[p].v+x);}
		inline void spread(int p)
		{
			down2(ls(p),t[p].tg3),down2(rs(p),t[p].tg3);
			down(ls(p),t[p].tg2),down(rs(p),t[p].tg2);
			t[p].tg2=t[p].tg3=0;
		}
		void build(int p,int l,int r)
		{
			t[p]={l,r,0,0,0,0};
			if(l==r)return;
			int mid=l+((r-l)>>1);
			build(ls(p),l,mid),build(rs(p),mid+1,r);
		}
		void modify(int p,int l,int r,ll x)
		{
			if(l<=t[p].l&&r>=t[p].r)return down(p,x);
			spread(p);
			if(l<=t[ls(p)].r)modify(ls(p),l,r,x);
			if(r>t[ls(p)].r)modify(rs(p),l,r,x);
			update(p);
		}
		ll ask(int p,int l,int r)
		{
			if(l<=t[p].l&&r>=t[p].r)return t[p].s;
			spread(p);
			if(r<=t[ls(p)].r)return ask(ls(p),l,r);
			if(l>t[ls(p)].r)return ask(rs(p),l,r);
			return max(ask(ls(p),l,r),ask(rs(p),l,r));
		}
		#undef ls
		#undef rs
	}
	using namespace Segment;
	namespace Segment2
	{
		#define ls(p) (t2[p].l+t2[p].r)
		#define rs(p) (ls(p)^1)
		#define fa(p) t2[p].fa
		vi T[18];
		struct{int l,r,fa;vi ve;}t2[132000];
		void build2(int p,int l,int r,int dp=0)
		{
			t2[p].l=l,t2[p].r=r,T[dp].eb(p);
			if(l==r)return;
			int mid=l+((r-l)>>1);
			fa(ls(p))=fa(rs(p))=p;
			build2(ls(p),l,mid,dp+1),build2(rs(p),mid+1,r,dp+1);
		}
		void push(int p,int l,int r,int id)
		{
			int mid=ls(p)>>1;
			if((l<=mid&&mid<r)||t2[p].r-t2[p].l==1)
			return t2[p].ve.eb(id),void();
			if(r<=mid)return push(ls(p),l,r,id);
			return push(rs(p),l,r,id);
		}
	}
	using namespace Segment2;
	const ll If=180000000000000;
	inline void mian()
	{
		read(n,m,q),n=65536,build2(1,1,n);int l1,l2,r1,r2;ll v;
		for(int i=1;i<=m;++i)
		{
			read(l1,l2,r1,r2,v);
			ve[l1].eb(tup(l2,r2,v));
			ev[r1].eb(tup(l2,r2,v));
		}
		for(int i=1;i<=q;++i)
		{
			read(b[i].l1,b[i].l2,b[i].r1,b[i].r2);
			b[i].id=i,push(1,b[i].l1,b[i].r1,i);
		}
		for(int i=1;i<=16;++i)
		{
			build(1,1,n);
			for(auto p:T[i-1])for(auto x:t2[p].ve)
			if(b[x].r1>t[ls(p)].r)
			qu[b[x].r1].eb(tup(b[x].l2,b[x].r2,x));
			ll sum=0;
			for(auto p:T[i])
			{
				if(p==rs(fa(p)))sum+=If,modify(1,1,n,If);
				for(int j=t2[p].l;j<=t2[p].r;++j)
				{
					for(auto x:ve[j])
					modify(1,x.x,x.y,x.z);
					for(auto x:qu[j])
					ans[x.z]=max(ans[x.z],ask(1,x.x,x.y)-sum);
					for(auto x:ev[j])
					modify(1,x.x,x.y,-x.z);
				}
			}
			for(auto p:T[i-1])for(auto x:t2[p].ve)qu[b[x].r1].clear();
			
			reverse(T[i].begin(),T[i].end());
			
			build(1,1,n),sum=0;
			for(auto p:T[i-1])for(auto x:t2[p].ve)
			if(b[x].l1<=t[ls(p)].r)
			qu[b[x].l1].eb(tup(b[x].l2,b[x].r2,x));
			for(auto p:T[i])
			{
				if(p==ls(fa(p)))sum+=If,modify(1,1,n,If);
				for(int j=t2[p].r;j>=t2[p].l;--j)
				{
					for(auto x:ev[j])modify(1,x.x,x.y,x.z);
					for(auto x:qu[j])ans[x.z]=max(ans[x.z],ask(1,x.x,x.y)-sum);
					for(auto x:ve[j])modify(1,x.x,x.y,-x.z);
				}
			}
			for(auto p:T[i-1])for(auto x:t2[p].ve)qu[b[x].l1].clear();
		}
		for(int i=1;i<=q;++i)write(ans[i],'\n');
	}
posted @ 2024-04-15 08:31  WrongAnswer_90  阅读(27)  评论(0编辑  收藏  举报