[EC-final 2022 G] Rectangle

发布时间 2023-04-17 23:23:29作者: 蒟蒻_william555

简要题意

给定 \(n\) 个边界与坐标轴平行的整点矩形,你可以画三条不同的平行于坐标轴的直线(\(x=k \or y=k,k\in Z \cap [1,10^9]\)),使得每个矩形至少与一条直线相交,问方案数。 \(n \le 1e5\)

题解

分类讨论,有两种情况:1、三条直线同向;2、有一条直线与另外两条垂直。

首先把横纵坐标离散化。

对于第一种情况,我们先计算都平行于 x 轴的方案数(平行于 y 轴就是旋转90°)。考虑枚举中间的那条直线的位置,然后计算两边的方案数。如果一个矩形的右边界都在这条直线的左边,那么它一定被左边的直线穿过,右边同理。于是我们分别算出左右两边矩形的 x 坐标的交集,就可以计算左右两边的方案数。这一部分是线性的。

难点主要在第二种情况,假设是一条平行于 x 轴的直线和两条平行于 y 轴的直线。枚举平行于 x 轴的这根直线的位置,将所有与它不相交的矩形的纵坐标区间拿出来。问题变为快速维护有多少种方案能选两个点与所有区间相交。记 \([L_i,R_i]\) 为左边的点位置在 \(i\),右边的那个点可以选择的区间。左边的点一定不超过所有区间右端点的最小值 \(\min r_j\),再此限制下,左边的点越靠右,右边的点的限制越松。同理,右边的点在不小于 \(\max l_j\) 的前提下,越靠左越好。所以 \(L_i\le R_i\) 的一定是以 \(\min r_j\) 结尾的一段区间,而每个 \(L_i=max(i,\max l_j)\)。于是我们可以快速维护 \(L\) 之和,然后只需计算 \(R\) 的和。

考虑 \(R\) 的限制从何而来,对于 \(i\)\(R_i=\min_{j,l_j>i} r_j\) 。其实就是把每个 \(r_j\) 挂在 \(l_j\) 上,然后对 \(i\) 求后缀 min。于是我们要做到的就是再由插入和删除的情况下支持快速查询一段区间的后缀 min 之和。这可以使用线段树来维护。具体的,每个节点维护区间最小值 \(mn_p\),区间后缀最小值之和 \(sum_p\)(只看这个区间内的数的后缀min),以及在有 \(mn_{rs}\) 的情况下,左子树的后缀最小值之和 \(sl_p\)。每次 pushup 的时候,\(sum_p=sum_{rs}+sl_p\),然后 \(sl_p\) 要用 \(mn_{rs}\) 到左子树二分计算。于是每次操作复杂度 \(O(log^2)\),实现了支持删除的后缀min之和的维护。

维护了 \(R\) 就能轻易算出答案,复杂度 \(O(nlog^2n)\),常数巨大。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int BS=1<<20|5;
char buf[BS],*P1,*P2;
inline char gc(){
	if(P1==P2)P2=(P1=buf)+fread(buf,1,BS,stdin);
	return P1==P2?EOF:*(P1++);
}
inline int in(){
	int x=0,f=1;char ch=gc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=gc();
	return x*f;
}
const int N=2e5+5,mod=998244353,inv6=(mod+1)/6,inf=1e9;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Add(int &a,int b){a+=b;if(a>=mod)a-=mod;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int C2(int x){return (1ll*x*(x-1)>>1)%mod;}
inline int C3(int x){return 1ll*x*(x-1)%mod*(x-2)%mod*inv6%mod;}
int n,xc,yc;
int x[N],y[N],xx[N],yy[N],X[N],Y[N];
int calc1(){
	static int lp[N],rp[N],fl[N],fr[N];
	for(int i=1;i<=xc;i++)lp[i]=0,rp[i]=xc+1;
	for(int i=1;i<=n;i++){
		lp[xx[i]]=max(lp[xx[i]],x[i]);
		rp[x[i]]=min(rp[x[i]],xx[i]);
	}
	int L=0,R=0;
	for(int i=1;i<=xc;i++){
		if(!R)fl[i]=-1;
		else fl[i]=max(0,X[R+1]-X[L]);
		if(lp[i]==0)continue;
		if(!R)R=i,L=lp[i];
		L=max(L,lp[i]);
	}
	L=0,R=0;
	for(int i=xc;i>=1;i--){
		if(!L)fr[i]=-1;
		else fr[i]=max(0,X[R+1]-X[L]);
		if(rp[i]==xc+1)continue;
		if(!L)L=i,R=rp[i];
		R=min(R,rp[i]);
	}
	int res=0;
	for(int i=1;i<=xc;i++){
		int val;
		if(fl[i]==-1&&fr[i]==-1){
			val=mul(X[i]-1,mul(X[i+1]-X[i],X[xc+1]-X[i+1]));
			Add(val,C3(X[i+1]-X[i]));
			Add(val,mul(C2(X[i+1]-X[i]),add(X[i]-1,X[xc+1]-X[i+1])));
		}else if(fl[i]==-1){
			val=add(mul(X[i]-1,X[i+1]-X[i]),C2(X[i+1]-X[i]));
			val=mul(val,fr[i]);
		}else if(fr[i]==-1){
			val=add(mul(X[xc+1]-X[i+1],X[i+1]-X[i]),C2(X[i+1]-X[i]));
			val=mul(val,fl[i]);
		}else{
			val=mul(mul(fl[i],fr[i]),X[i+1]-X[i]);
		}
		Add(res,val);
	}
	return res;
}
int mn[N<<2];
ll sum[N<<2],sl[N<<2];
int len[N<<2];
struct PQ{
	priority_queue<int> pq1,pq2;
	void clear(){
		while(pq1.size())pq1.pop();
		while(pq2.size())pq2.pop();
	}
	void ins(int x){
		pq1.push(-x);
	}
	void del(int x){
		pq2.push(-x);
	}
	int top(){
		while(pq2.size()&&pq1.top()==pq2.top())pq1.pop(),pq2.pop();
		if(pq1.size())return -pq1.top();
		return inf;
	}
	int size(){
		return pq1.size()-pq2.size();
	}
}pq[N];
void build(int p,int l,int r){
	mn[p]=1e9,sum[p]=mul(len[p]=Y[r+1]-Y[l],mn[p]),sl[p]=0;
	if(l==r){
		pq[l].clear();
		return;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);
	sl[p]=sum[p<<1];
}
ll getsl(int p,int l,int r,int v){
	if(mn[p]>=v)return 1ll*len[p]*v;
	if(l==r)return sum[p];
	int mid=l+r>>1;ll res=0;
	res=getsl(p<<1|1,mid+1,r,v);
	if(mn[p<<1|1]>v)res+=getsl(p<<1,l,mid,v);
	else res+=sl[p];
	return res;
}
inline void pushup(int p,int l,int mid){
	mn[p]=min(mn[p<<1],mn[p<<1|1]);
	sum[p]=sum[p<<1|1];
	if(mn[p]==mn[p<<1|1]){
		sl[p]=1ll*len[p<<1]*mn[p<<1|1];
	}else sl[p]=getsl(p<<1,l,mid,mn[p<<1|1]);
	sum[p]+=sl[p];
}
void modify(int p,int l,int r,int d,int v){
	if(l==r){
		if(v>0)pq[l].ins(v);
		else pq[l].del(-v);
		mn[p]=pq[l].top();
		sum[p]=1ll*mn[p]*len[p];
		return;
	}
	int mid=l+r>>1;
	if(d<=mid)modify(p<<1,l,mid,d,v);
	else modify(p<<1|1,mid+1,r,d,v);
	pushup(p,l,mid);
}
int getmin(int p,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return mn[p];
	int mid=l+r>>1,res=1e9;
	if(ql<=mid)res=getmin(p<<1,l,mid,ql,qr);
	if(mid<qr)res=min(res,getmin(p<<1|1,mid+1,r,ql,qr));
	return res;
}
int now;
ll query(int p,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){
		if(mn[p]>=now)return 1ll*len[p]*now;
		int tmp=now;now=mn[p];
		return getsl(p,l,r,tmp);
	}
	int mid=l+r>>1;ll res=0;
	if(mid<qr)res=query(p<<1|1,mid+1,r,ql,qr);
	now=min(now,mn[p<<1|1]);
	if(ql<=mid)res+=query(p<<1,l,mid,ql,qr);
	return res;
}
vector<int> v1[N],v2[N];
PQ pql,pqr;
void ins(int p){
	pql.ins(-y[p]),pqr.ins(yy[p]);
	if(y[p]==1)return;
	modify(1,1,yc,y[p]-1,Y[yy[p]+1]-1);
}
void del(int p){
	pql.del(-y[p]),pqr.del(yy[p]);
	if(y[p]==1)return;
	modify(1,1,yc,y[p]-1,-(Y[yy[p]+1]-1));
}
int ask(){
	int L=1,R=yc;
	if(pqr.size())R=abs(pqr.top());
	if(pql.size())L=abs(pql.top());
	int l=1,r=R,mid;
	while(l<=r){
		mid=l+r>>1;
		if(getmin(1,1,yc,mid,yc)>=Y[max(mid,L)])r=mid-1;
		else l=mid+1;
	}
	int pos=r+1;
	if(pos>R)return 0;
	long long suml=0;
	if(L>R)suml=1ll*(Y[L]-1)*(Y[R+1]-Y[pos]);
	else if(L>pos){
		suml=1ll*(Y[L]-1)*(Y[L]-Y[pos]);
		suml+=1ll*(Y[R+1]+Y[L]-1)*(Y[R+1]-Y[L])/2;
	}else suml=1ll*(Y[R+1]+Y[pos]-1)*(Y[R+1]-Y[pos])/2;
	suml%=mod;
	now=1e9+1;
	long long sumr=query(1,1,yc,pos,R)%mod;
	return add(sumr,mod-suml);
}
int calc2(){
	for(int i=1;i<=xc;i++)v1[i].clear(),v2[i].clear();
	for(int i=1;i<=n;i++){
		v1[x[i]].push_back(i);
		v2[xx[i]].push_back(i);
	}
	build(1,1,yc);pql.clear(),pqr.clear();
	for(int i=1;i<=n;i++)ins(i);
	int res=0;
	for(int i=1;i<=xc;i++){
		for(int j:v1[i])del(j);
		Add(res,mul(X[i+1]-X[i],ask()));
		for(int j:v2[i])ins(j);
	}
	return res;
}
void solve(){
	n=in();xc=yc=0;
	for(int i=1;i<=n;i++){
		x[i]=in(),y[i]=in(),xx[i]=in(),yy[i]=in();
		X[++xc]=x[i],X[++xc]=xx[i]+1;
		Y[++yc]=y[i],Y[++yc]=yy[i]+1;
	}
	X[++xc]=1,Y[++yc]=1;X[++xc]=1e9+1,Y[++yc]=1e9+1;
	sort(X+1,X+xc+1),sort(Y+1,Y+yc+1);
	xc=unique(X+1,X+xc+1)-X-1;
	yc=unique(Y+1,Y+yc+1)-Y-1;
	xc--,yc--;
	for(int i=1;i<=n;i++){
		x[i]=lower_bound(X+1,X+xc+1,x[i])-X;
		y[i]=lower_bound(Y+1,Y+yc+1,y[i])-Y;
		xx[i]=upper_bound(X+1,X+xc+1,xx[i])-X-1;
		yy[i]=upper_bound(Y+1,Y+yc+1,yy[i])-Y-1;
	}
	int ans=0;
	Add(ans,calc1());Add(ans,calc2());
	for(int i=1;i<=max(xc,yc)+1;i++)swap(X[i],Y[i]);
	for(int i=1;i<=n;i++)swap(x[i],y[i]),swap(xx[i],yy[i]);
	swap(xc,yc);
	Add(ans,calc1()),Add(ans,calc2());
	printf("%d\n",ans);
}
int main(){
	int T=in();
	while(T--)solve();
	return 0;
}