Title

CF1401E Divide Square 题解

解题思路

其实多看几组也能发现块数等于交点的数量加上两个端点都在边上的线段数量再加一。

证明如下(图见样例):

  • 对于两条只有一个端点位于边上的线段,因为保证有一个端点位于边上,那么这两条线段的交点一定会和已存在的点、边构成一个新的矩形;
  • 对于其中有一条为两个端点均位于边上的两条线段,两个端点均位于边上的线段会与已存在的两边构成两个矩形;
  • 剩下的不规则部分产生 1 的贡献。

此时问题转化为如何计算出交点数量。

考虑枚举横坐标,然后统计出此时这个坐标上有多少条横向线段,然后判断是否有一条竖线的横坐标为当前横坐标,如果有,则分以下两种情况计算:

  1. 存在一个端点位于 x 轴上,那么直接将答案加上纵坐标小于等于 ri 的横线数量即可;
  2. 其他情况,不妨设当前横坐标上有 sum 条横向线段,那么答案应加上 sum 减去纵坐标小于 li 的横线数量。

那么,我们需要做到快速维护横向线段。考虑使用树状数组。设当前横坐标为 x,设横线段左端点横坐标为 lai,右端点横坐标为 rai,纵坐标为 yi。那么我们分别对于横线按照 lai 从小到大和 rai 从大到小排序,然后判断若 lai=x,那么在 yi 位置加上 1;若 ra1=x,那么在 yi 位置减去 1。然后判断若 x 处有纵线段,那么直接查找即可。

本题步骤如下:

  • 分别存储横、纵线段,横线段存两份;
  • 将纵线段按 x 排序,两份横线段分别按 lai 从小到大和 rai 从大到小排序;
  • 枚举 x,更新当前位置上的横线段数量;
  • 判断是否存在一条竖线段,若有则按上文所讲方法更新 ans 即可;
  • 枚举每条线段,若该线段两个端点都在正方形边上,那么 ansans+1
  • 输出 ans+1

注意事项

  • 不开 long long 见祖宗;
  • 不需要考虑树状数组越界问题;
  • 先添加线段,然后计算,最后再删;
  • 在计算 r 位于边上的线段时,一定要查询小于等于 l1 的横线数量,而不是 l

AC 代码

#include<set>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define int long long
#define N 100005
#define mint 0
#define maxt 1000000
int n,m;
struct Line{
    int y,l,r;
}a[N],d[N];
struct lIne{
    int x,l,r;
}b[N];
inline bool cmp(Line A,Line B){
    return A.l<B.l;
}
inline bool Cmp(lIne A,lIne B){
    return A.x<B.x;
}
inline bool CMP(Line A,Line B){
    return A.r<B.r;
}
struct BitTree{
    int val[(maxt<<2)+5];
    #define lowbit(x) (x&(-x))
    inline void Add(int x,int v){
        while(x<=maxt){
            val[x]+=v;
            x+=lowbit(x);
        }
    }
    inline int Query(int x){
        int res=0;
        while(x){
            res+=val[x];
            x-=lowbit(x);
        }return res;
    }
}tree;
signed main(){
    scanf("%lld%lld",&n,&m);
    for(register int i=1;i<=n;++i){
        scanf("%lld",&a[i].y);
        scanf("%lld",&a[i].l);
        scanf("%lld",&a[i].r);
    }
    for(register int i=1;i<=m;++i){
        scanf("%lld",&b[i].x);
        scanf("%lld",&b[i].l);
        scanf("%lld",&b[i].r);
    }
    for(register int i=1;i<=n;++i)
        d[i]=a[i];
    std::sort(a+1,a+n+1,cmp);
    std::sort(d+1,d+n+1,CMP);
    std::sort(b+1,b+m+1,Cmp);
    int la=1,ra=1,lb=1,ans=0,cnt=0;
    for(register int x=mint;x<=maxt;++x){
        while(a[la].l==x&&la<=n){
            tree.Add(a[la].y,1);
            ++la;++cnt;
        }
        if(x==b[lb].x){
            if(b[lb].l==mint&&b[lb].r==maxt)
                ++ans;
            if(b[lb].l==mint)
                ans+=tree.Query(b[lb].r);
            else
                ans+=cnt-tree.Query(b[lb].l-1);
            ++lb;
        }while(d[ra].r==x&&ra<=n){
            tree.Add(d[ra].y,-1);
            ++ra;--cnt;
        }
    }for(register int i=1;i<=n;++i)
        if(a[i].l==mint&&a[i].r==maxt)
            ++ans;
    printf("%lld",ans+1ll);
}
posted @   UncleSam_Died  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示