题目大意:给定$N$个矩形,求连通块个数。($1 \leq N,x_1,x_2,y_1,y_2 \leq 100000$)
乍一看就能知道是扫描线,不过这题的细节恐怖的要命。
(std同样看不懂,自己魔改了一下)
首先把完全相同的矩形去掉。
之后咱们可以发现,被其他矩形完全包含的小矩形对答案没有任何贡献,所以可以去除。
这一步可以用三维偏序来实现,即对于每一个$i$,如果$\exists j , j_{x1} \leq i_{x1},j_{x2} \geq i_{x2},j_{y1} \leq i_{y1},j_{y2} \geq i_{y2}$,则说明矩形$i$被完全包含。
把前三位cdq,求最后一维的最大值就行。
之后我们会发现,对于两个矩形来说,他们相交当且仅当他们有至少一对边相交,于是这个问题就变成了给你一堆线段,求线段的连通块个数,因为矩形的四条边一定联通。
我们考虑,可以将当前的竖线插入线段树,同时线段树维护区间竖线个数(一会有用),以及每个下标竖线的id(因为如果有x坐标相同的,一定相交联通,属于一个并查集,所以只记录一个即可)。
之后考虑横线,对于一条横线$[l,r]$来说,他与当前$[l,r]$内所有竖线相交,可以利用线段树查询,但是这就出现了一个问题,查询会查到和这条横线在同一并查集的竖线,这就麻烦了。我们就开 线段个数 个线段树,以及一棵总线段树,在修改时同时修改并查集根所在线段树以及总线段树,然后查询时直接用总线段树的个数减当前并查集根线段树的个数。
在线段树上二分,查找最左面的不在本并查集的竖线。每次找到,就将其并查集与本并查集合并,同时将两棵线段树合并。
细节特别多。
大概是std速度的1.5~2倍。
using namespace std; #include<bits/stdc++.h> namespace zhu{ #define MY_FILE "d" void file(){ freopen(MY_FILE ".in","r",stdin); freopen(MY_FILE ".out","w",stdout); } const int maxn=2e5+5; #define MAXN 100000 void ckm(int &x,const int y){ if(y<x) x=y; } int N; struct mt{ int x1,x2,y1,y2; bool ig; friend bool operator == (const mt &a,const mt &b){ return a.x1==b.x1&&a.x2==b.x2&&a.y1==b.y1&&a.y2==b.y2; } friend bool operator != (const mt &a,const mt &b){ return !(a==b); } }m[maxn]; namespace __cdq{ bool cmp2(const mt &a,const mt &b){ if(a.x2==b.x2) return a.y1<b.y1; else return a.x2<b.x2; } int tr[maxn]; #define lb(x) (x&-x) void c(int x,int w){ for(;x<=MAXN;x+=lb(x)) ckm(tr[x],w); } void dl(int x){ for(;x<=MAXN;x+=lb(x)) tr[x]=0x7fffffff; } int q(int x){ int p=0x7fffffff; for(;x;x-=lb(x)) ckm(p,tr[x]); return p; } #define M ((l+r)>>1) void cdq(int l,int r){ if(l==r) return; cdq(l,M),cdq(M+1,r); sort(m+l,m+M+1,cmp2); sort(m+M+1,m+r+1,cmp2); for(int i=l,j=M+1;j<=r;j++){ while(i<=M&&m[i].x2<=m[j].x2) c(m[i].y1,m[i].y2),i++; m[j].ig|=(q(m[j].y1)<=m[j].y2); } for(int i=l;i<=M;i++) dl(m[i].y1); } void Slove(){ for(int i=0;i<=MAXN;i++) tr[i]=0x7fffffff; for(int i=1;i<=N;i++) m[i].x2=MAXN+1-m[i].x2,m[i].y2=MAXN+1-m[i].y2; // x1<x1 x2<x2 y1<y1 qu y2 max auto cmp1=[&](const mt &a,const mt &b){ if(a.x1==b.x1){ if(a.x2==b.x2) return a.y1<b.y1; else return a.x2<b.x2; }else return a.x1<b.x1; }; sort(m+1,m+N+1,cmp1); int j=0; for(int i=1;i<=N;i++){ if(i==1||m[i]!=m[i-1])m[++j]=m[i]; } N=j; cdq(1,N); for(int i=1;i<=N;i++) m[i].x2=MAXN+1-m[i].x2,m[i].y2=MAXN+1-m[i].y2; j=0; for(int i=1;i<=N;i++){ if(!m[i].ig)m[++j]=m[i]; } N=j; } } struct hx{ int l,r; int id; }; vector<hx>h[maxn]; struct sx{ int p,id; }; vector<sx>adds[maxn],dels[maxn]; int f[maxn<<2]; int find(int x){return f[x]==x?x:f[x]=find(f[x]);} int root[maxn<<2],tmp=0; const int maxt=maxn*200; int ls[maxt],rs[maxt],tr[maxt]; void pushup(int rt){ tr[rt]=tr[ls[rt]]+tr[rs[rt]]; } int merge(int x,int y,int l,int r){ if(!x||!y) return x|y; if(!tr[x]) return y; if(!tr[y]) return x; if(l==r) return tr[x]+=tr[y],x; ls[x]=merge(ls[x],ls[y],l,M); rs[x]=merge(rs[x],rs[y],M+1,r); pushup(x); return x; } int num[maxn]; void ch(int &rt,int l,int r,int c,int w,int t){ if(!rt) rt=++tmp; if(l==r){ if(w>0) num[l]=t; tr[rt]+=w; if(tr[rt]==0) num[l]=0; return; } if(c<=M) ch(ls[rt],l,M,c,w,t); else ch(rs[rt],M+1,r,c,w,t); pushup(rt); } int find(int rt,int rtk,int l,int r,int L,int R){ if(!rt) return -1; if(R<l||r<L) return -1; if(l==r) return tr[rt]==tr[rtk]?-1:l; if(L<=l&&r<=R){ if(tr[ls[rt]]-tr[ls[rtk]]>0) return find(ls[rt],ls[rtk],l,M,L,R); else return find(rs[rt],rs[rtk],M+1,r,L,R); } int x=find(ls[rt],ls[rtk],l,M,L,R); if(~x) return x; else return find(rs[rt],rs[rtk],M+1,r,L,R); } int MAIN(){ file(); cin>>N; for(int i=1;i<=N;i++){ int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); // h[b].push_back((hx){a,c}); // h[d].push_back((hx){a,c}); m[i]=(mt){a,c,b,d,0}; } __cdq::Slove(); for(int i=1;i<=N;i++){ int x1=m[i].x1,x2=m[i].x2,y1=m[i].y1,y2=m[i].y2; h[y1].push_back((hx){x1,x2,i*4-3}); h[y2].push_back((hx){x1,x2,i*4-2}); adds[y1].push_back((sx){x1,i*4-1}),adds[y1].push_back((sx){x2,i*4}); dels[y2+1].push_back((sx){x1,i*4-1}),dels[y2+1].push_back((sx){x2,i*4}); } for(int i=1;i<=N<<2;i++) f[i]=i; for(int i=1;i<=MAXN;i++){ for(auto c:dels[i]){ ch(root[find(c.id)],1,MAXN,c.p,-1,0); ch(root[0],1,MAXN,c.p,-1,0); } for(auto c:adds[i]){ if(num[c.p]){ root[c.id]=root[find(num[c.p])]; f[find(num[c.p])]=c.id; } ch(root[c.id],1,MAXN,c.p,1,c.id); ch(root[0],1,MAXN,c.p,1,c.id); } for(auto c:h[i]){ int a=c.l,b=c.r,id=c.id; while(1){ int x=find(root[0],root[id],1,MAXN,a,b); if(x==-1) break; root[id]=merge(root[id],root[find(num[x])],1,MAXN); f[find(num[x])]=id; } } } int ans=0; for(int i=1;i<=N<<2;i++) ans+=find(i)==i; cout<<ans<<endl; return 0; } }; #undef int using namespace zhu; int main(){ return MAIN(); }