【蓝桥杯国赛】奇偶覆盖

标准的二维扫描线模板题目。
在普通扫描线的基础上,要用离散化+线段树去优化。

普通扫描线

391数飞机
在一个数轴上扫描区间,可以规避掉:判断每一个点被多少个区间覆盖。
我们可以直接判断左右边界,有时左右边界集会间隔很大,为了建立线段树方便,或者为了减少访问次数,要先对数据集进行离散化。
对边界集进行排序,一段边界:左边界加一个权值1,右边界加一个权值-1。排序过后,用cnt记录从小到大的边界点的权值和,动态维护一个最大值,该值就是被最多区间覆盖的区域的覆盖区间个数。

离散化

对每一个分散的点进行编号,排序,这样在找一个点的真实值的时候,可以用二分去找到它的下标。

线段树

完成区间更新,区间查询的任务。

离散化+线段树的二维扫描线

洛谷P5490 [二维扫描线模板]
蓝桥杯 奇偶覆盖 [稍微变形]
【题目】给定n个矩形,每个矩形给左下角,右上角坐标。问被奇数个、偶数个矩形覆盖的总面积。
首先先确定一点:我们确定先对横坐标做一次扫描线的初始化,离散化。
当离散化完成,就可以便利的完成线段树的建树。因为这样的话区间都是可以一分为二的。
定义扫描线的结构体,包含:左端点、右端点、当前高度、是下边界or上边界(1 or -1)。
进行离散化,对X[]数组排序;对结构体按照高度由低到高排序。
tot本质不同的左右端点个数。
建树(可以不建,初始化而已,反正本来就都是0)
按照高度,开始遍历结构体,更新线段树。每一次都找到左右端点的位置,矩形线的范围;同时代入[1,tot],需要更新的范围;根节点1;当前范围的状态,下界or上界。
更新完成后维护一下面积和。区间长度乘以高度差
线段树更新细节:
两部分:pushup,向上更新,也就是我递归到最小[l,r]范围后,得到确定结果,然后再回溯到父节点更新,一直更新到根节点;update,更新操作,去卡范围。

#include<bits/stdc++.h>
using namespace std;
#define lson (x<<1)
#define rson (x<<1|1)
typedef long long ll;
const int N=7e5+100;
int n,cnt;
ll xx1,xx2,yy1,yy2,X[N<<1];
struct scanline{
    ll l,r,h;
    int mark;
    bool operator < (const scanline &rhs) const {
        return h<rhs.h;
    }
}line[N<<1];
struct segtree{
    int l,r,sum;
    ll len1,len2;//1 ji 2 ou
}tree[N<<1];
void build(int x,int l,int r){
    tree[x].l=l,tree[x].r=r;
    tree[x].len1=0;
    tree[x].len2=0;
    tree[x].sum=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    return ;
}
void pushup(int x,int l,int r){
    if(tree[x].sum){
        if(tree[x].sum&1){
            tree[x].len2=tree[lson].len1+tree[rson].len1;//注意坑点 奇+奇=偶
            tree[x].len1=(X[r+1]-X[l])-tree[x].len2;
        }else{
            tree[x].len1=tree[lson].len1+tree[rson].len1;
            tree[x].len2=(X[r+1]-X[l])-tree[x].len1;
        }
    }else{
        tree[x].len1=tree[lson].len1+tree[rson].len1;
        tree[x].len2=tree[lson].len2+tree[rson].len2;//与上面不同,仅统计个数
    }
}
void update(int L,int R,int c,int l,int r,int x){
    if(L<=l&&R>=r){
        tree[x].sum+=c;
        pushup(x,l,r);
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,c,l,mid,lson);
    if(R>mid) update(L,R,c,mid+1,r,rson);
    pushup(x,l,r);
}
int get(int x,int tot){
    return lower_bound(X+1,X+tot+1,x)-X;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>xx1>>yy1>>xx2>>yy2;
        X[2*i-1]=xx1,X[2*i]=xx2;
        line[2*i-1]=(scanline){xx1,xx2,yy1,1};
        line[2*i]=(scanline){xx1,xx2,yy2,-1};
    }
    n<<=1;
    sort(line+1,line+n+1);
    sort(X+1,X+n+1);
    int tot=unique(X+1,X+n+1)-X-1;
    build(1,1,tot-1);
    ll ans1=0,ans2=0;
    for(int i=1;i<n;i++){
        update(get(line[i].l,tot),get(line[i].r,tot)-1,line[i].mark,1,tot,1);
        ans1+=tree[1].len1*(line[i+1].h-line[i].h);
        ans2+=tree[1].len2*(line[i+1].h-line[i].h);
    }
    cout<<ans1<<endl;
    cout<<ans2<<endl;
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值