ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

模拟赛 矩形 (扫描线,三维偏序,线段树合并,并查集,线段树上二分)

2022-08-29 00:33:52  阅读:132  来源: 互联网

标签:偏序 rt return int 线段 扫描线 矩形 竖线


PRO

题目大意:给定$N$个矩形,求连通块个数。($1 \leq N,x_1,x_2,y_1,y_2 \leq 100000$)

SOL

乍一看就能知道是扫描线,不过这题的细节恐怖的要命。

(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();
}

 

标签:偏序,rt,return,int,线段,扫描线,矩形,竖线
来源: https://www.cnblogs.com/lnxxqz/p/16634514.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有