[EC-final 2022 G] Rectangle
简要题意
给定 个边界与坐标轴平行的整点矩形,你可以画三条不同的平行于坐标轴的直线(),使得每个矩形至少与一条直线相交,问方案数。 。
题解
分类讨论,有两种情况:1、三条直线同向;2、有一条直线与另外两条垂直。
首先把横纵坐标离散化。
对于第一种情况,我们先计算都平行于 x 轴的方案数(平行于 y 轴就是旋转90°)。考虑枚举中间的那条直线的位置,然后计算两边的方案数。如果一个矩形的右边界都在这条直线的左边,那么它一定被左边的直线穿过,右边同理。于是我们分别算出左右两边矩形的 x 坐标的交集,就可以计算左右两边的方案数。这一部分是线性的。
难点主要在第二种情况,假设是一条平行于 x 轴的直线和两条平行于 y 轴的直线。枚举平行于 x 轴的这根直线的位置,将所有与它不相交的矩形的纵坐标区间拿出来。问题变为快速维护有多少种方案能选两个点与所有区间相交。记 为左边的点位置在 ,右边的那个点可以选择的区间。左边的点一定不超过所有区间右端点的最小值 ,再此限制下,左边的点越靠右,右边的点的限制越松。同理,右边的点在不小于 的前提下,越靠左越好。所以 的一定是以 结尾的一段区间,而每个 。于是我们可以快速维护 之和,然后只需计算 的和。
考虑 的限制从何而来,对于 , 。其实就是把每个 挂在 上,然后对 求后缀 min。于是我们要做到的就是再由插入和删除的情况下支持快速查询一段区间的后缀 min 之和。这可以使用线段树来维护。具体的,每个节点维护区间最小值 ,区间后缀最小值之和 (只看这个区间内的数的后缀min),以及在有 的情况下,左子树的后缀最小值之和 。每次 pushup 的时候,,然后 要用 到左子树二分计算。于是每次操作复杂度 ,实现了支持删除的后缀min之和的维护。
维护了 就能轻易算出答案,复杂度 ,常数巨大。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现