解题思路
其实多看几组也能发现块数等于交点的数量加上两个端点都在边上的线段数量再加一。
证明如下(图见样例):
- 对于两条只有一个端点位于边上的线段,因为保证有一个端点位于边上,那么这两条线段的交点一定会和已存在的点、边构成一个新的矩形;
- 对于其中有一条为两个端点均位于边上的两条线段,两个端点均位于边上的线段会与已存在的两边构成两个矩形;
- 剩下的不规则部分产生 \(1\) 的贡献。
此时问题转化为如何计算出交点数量。
考虑枚举横坐标,然后统计出此时这个坐标上有多少条横向线段,然后判断是否有一条竖线的横坐标为当前横坐标,如果有,则分以下两种情况计算:
- 存在一个端点位于 x 轴上,那么直接将答案加上纵坐标小于等于 \(r_i\) 的横线数量即可;
- 其他情况,不妨设当前横坐标上有 \(sum\) 条横向线段,那么答案应加上 \(sum\) 减去纵坐标小于 \(l_i\) 的横线数量。
那么,我们需要做到快速维护横向线段。考虑使用树状数组。设当前横坐标为 \(x\),设横线段左端点横坐标为 \(la_i\),右端点横坐标为 \(ra_i\),纵坐标为 \(y_i\)。那么我们分别对于横线按照 \(la_i\) 从小到大和 \(ra_i\) 从大到小排序,然后判断若 \(la_i=x\),那么在 \(y_i\) 位置加上 \(1\);若 \(ra_1=x\),那么在 \(y_i\) 位置减去 \(1\)。然后判断若 \(x\) 处有纵线段,那么直接查找即可。
本题步骤如下:
- 分别存储横、纵线段,横线段存两份;
- 将纵线段按 \(x\) 排序,两份横线段分别按 \(la_i\) 从小到大和 \(ra_i\) 从大到小排序;
- 枚举 \(x\),更新当前位置上的横线段数量;
- 判断是否存在一条竖线段,若有则按上文所讲方法更新 \(ans\) 即可;
- 枚举每条线段,若该线段两个端点都在正方形边上,那么 \(ans\gets ans+1\);
- 输出 \(ans+1\);
注意事项
- 不开 long long 见祖宗;
- 不需要考虑树状数组越界问题;
- 先添加线段,然后计算,最后再删;
- 在计算 \(r\) 位于边上的线段时,一定要查询小于等于 \(l-1\) 的横线数量,而不是 \(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);
}
标签:CF1401E,Square,边上,题解,线段,la,横坐标,横线,端点
From: https://www.cnblogs.com/UncleSamDied6/p/18010661