【学习笔记】线段树 之 扫描线
I
\mathrm{I}
I 概况
扫描线就是解决一些在二维平面内处理矩形相交的一系列问题。我们在这里具体地讲两个:矩形面积并以及周长并 若不喜,勿喷。
I
I
\mathrm{II}
I I 矩形面积并
例题模板 面积并相对周长来说比较简单。 首先我们来大致了解一下扫描线地工作原理:我们每次加入一个矩阵的操作相当于左边那条边
+
1
+1
+ 1 ,右边那条
−
1
-1
− 1 (有点类似于差分的性质)。画张图来看看:
相当于每次加入矩形一条边我们就去寻找与其对应的
−
1
-1
− 1 边,而面积就是这两条线之间的距离
×
\times
× 高(相当于我们把这个图分成若干个矩形来求解) 对于那些
x
i
,
y
i
x_i,y_i
x i , y i 大的要进行离散化
C
o
d
e
\mathrm{Code}
C o d e
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
inline int read ( )
{
int sum= 0 , ff= 1 ; char ch= getchar ( ) ;
while ( ! isdigit ( ch) )
{
if ( ch== '-' ) ff= - 1 ;
ch= getchar ( ) ;
}
while ( isdigit ( ch) )
sum= sum* 10 + ( ch^ 48 ) , ch= getchar ( ) ;
return sum* ff;
}
const int N= 4e5 + 5 ;
int n, m, X[ N] , Y[ N] , ans;
struct nood
{
int l, r, y, inv;
inline bool friend operator < ( const nood & s, const nood & t)
{
return s. y< t. y;
}
} ;
nood a[ N] ;
struct Seg
{
int ok[ N* 4 ] , T[ N* 4 ] ;
inline void up ( int rt, int l, int r)
{
if ( ok[ rt] ) T[ rt] = X[ r+ 1 ] - X[ l] ;
else T[ rt] = T[ rt<< 1 ] + T[ rt<< 1 | 1 ] ;
}
inline void update ( int rt, int l, int r, int ll, int rr, int inv)
{
if ( ll<= l&& r<= rr)
{
ok[ rt] + = inv;
up ( rt, l, r) ;
return ;
}
int mid= ( l+ r) / 2 ;
if ( ll<= mid)
update ( rt<< 1 , l, mid, ll, rr, inv) ;
if ( rr> mid)
update ( rt<< 1 | 1 , mid+ 1 , r, ll, rr, inv) ;
up ( rt, l, r) ;
}
} ;
Seg T;
signed main ( )
{
n= read ( ) ;
for ( int i= 1 ; i<= n; i++ )
{
int sx, sy, ex, ey;
sx= read ( ) , sy= read ( ) ;
ex= read ( ) , ey= read ( ) ;
X[ i* 2 - 1 ] = sx;
X[ i* 2 ] = ex;
a[ i* 2 - 1 ] = ( nood) { sx, ex, sy, 1 } ;
a[ i* 2 ] = ( nood) { sx, ex, ey, - 1 } ;
}
sort ( X+ 1 , X+ 2 * n+ 1 ) ;
sort ( a+ 1 , a+ 2 * n+ 1 ) ;
m= unique ( X+ 1 , X+ 2 * n+ 1 ) - X- 1 ;
for ( int i= 1 ; i< 2 * n; i++ )
{
int l= lower_bound ( X+ 1 , X+ m+ 1 , a[ i] . l) - X;
int r= lower_bound ( X+ 1 , X+ m+ 1 , a[ i] . r) - X;
T. update ( 1 , 1 , m, l, r- 1 , a[ i] . inv) ;
ans= ( ans+ ( a[ i+ 1 ] . y- a[ i] . y) * T. T[ 1 ] ) ;
}
printf ( "%lld\n" , ans) ;
return 0 ;
}
I
I
I
\mathrm{III}
I I I 矩形周长并
例题模板 我们对于周长并我们考虑这样去计算贡献(看图 我们每次只计算红,绿边的贡献。我们考虑分开计算:绿边的就是一段区间线段的个数
×
\times
× 长度
×
2
\times 2
× 2 那么我们要维护
5
5
5 个东西
T
[
k
]
.
o
k
T[k].ok
T [ k ] . o k :是否被覆盖
T
[
k
]
.
n
u
m
T[k].num
T [ k ] . n u m :被覆盖了几次
T
[
k
]
.
l
e
n
T[k].len
T [ k ] . l e n :被覆盖的长度
T
[
k
]
.
l
s
T[k].ls
T [ k ] . l s :左区间是否被覆盖
T
[
k
]
.
r
s
T[k].rs
T [ k ] . r s :右区间是否被覆盖 对于
l
e
n
,
o
k
,
l
s
,
r
s
len,ok,ls,rs
l e n , o k , l s , r s 都是很简单地维护一下就可以了,那么对于
n
u
m
num
n u m 我们还要减掉左区间右端点以及右区间左端点一起被覆盖地情况(因为这两条线段也会构成一条更长的线段 于是
T
[
k
]
.
n
u
m
=
T
[
k
∗
2
]
.
n
u
m
+
T
[
k
∗
2
+
1
]
.
n
u
m
−
(
T
[
k
∗
2
]
.
l
s
&
T
[
k
∗
2
+
1
]
.
r
s
)
T[k].num=T[k*2].num+T[k*2+1].num-(T[k*2].ls\And T[k*2+1].rs)
T [ k ] . n u m = T [ k ∗ 2 ] . n u m + T [ k ∗ 2 + 1 ] . n u m − ( T [ k ∗ 2 ] . l s & T [ k ∗ 2 + 1 ] . r s )
C
o
d
e
\mathrm{Code}
C o d e
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
inline int read ( )
{
int sum= 0 , ff= 1 ; char ch= getchar ( ) ;
while ( ! isdigit ( ch) )
{
if ( ch== '-' ) ff= - 1 ;
ch= getchar ( ) ;
}
while ( isdigit ( ch) )
sum= sum* 10 + ( ch^ 48 ) , ch= getchar ( ) ;
return sum* ff;
}
const int N= 4e5 + 5 ;
int n, m, X[ N] , Y[ N] , ans;
struct nood
{
int l, r, y, inv;
inline bool friend operator < ( const nood & s, const nood & t)
{
if ( s. y== t. y) return s. inv> t. inv;
return s. y< t. y;
}
} ;
nood a[ N] ;
struct Seg
{
struct seg
{
int num, len, ls;
int rs, ok;
} ;
seg T[ N* 4 ] ;
inline void up ( int rt, int l, int r)
{
if ( T[ rt] . ok)
{
T[ rt] . len= X[ r+ 1 ] - X[ l] ;
T[ rt] . num= 1 ;
T[ rt] . ls= T[ rt] . rs= 1 ;
}
else
{
T[ rt] . len= T[ rt<< 1 ] . len+ T[ rt<< 1 | 1 ] . len;
T[ rt] . num= T[ rt<< 1 ] . num+ T[ rt<< 1 | 1 ] . num- ( T[ rt<< 1 ] . rs&& T[ rt<< 1 | 1 ] . ls) ;
T[ rt] . ls= T[ rt<< 1 ] . ls;
T[ rt] . rs= T[ rt<< 1 | 1 ] . rs;
}
}
inline void update ( int rt, int l, int r, int ll, int rr, int inv)
{
if ( ll<= l&& r<= rr)
{
T[ rt] . ok+ = inv;
up ( rt, l, r) ;
return ;
}
int mid= ( l+ r) / 2 ;
if ( ll<= mid)
update ( rt<< 1 , l, mid, ll, rr, inv) ;
if ( rr> mid)
update ( rt<< 1 | 1 , mid+ 1 , r, ll, rr, inv) ;
up ( rt, l, r) ;
}
} ;
Seg T;
signed main ( )
{
n= read ( ) ;
for ( int i= 1 ; i<= n; i++ )
{
int sx, sy, ex, ey;
sx= read ( ) , sy= read ( ) ;
ex= read ( ) , ey= read ( ) ;
X[ i* 2 - 1 ] = sx;
X[ i* 2 ] = ex;
a[ i* 2 - 1 ] = ( nood) { sx, ex, sy, 1 } ;
a[ i* 2 ] = ( nood) { sx, ex, ey, - 1 } ;
}
sort ( X+ 1 , X+ 2 * n+ 1 ) ;
sort ( a+ 1 , a+ 2 * n+ 1 ) ;
m= unique ( X+ 1 , X+ 2 * n+ 1 ) - X- 1 ;
int laspos= 0 ;
for ( int i= 1 ; i<= 2 * n; i++ )
{
int l= lower_bound ( X+ 1 , X+ m+ 1 , a[ i] . l) - X;
int r= lower_bound ( X+ 1 , X+ m+ 1 , a[ i] . r) - X;
T. update ( 1 , 1 , m, l, r- 1 , a[ i] . inv) ;
ans= ans+ abs ( T. T[ 1 ] . len- laspos) ;
ans= ans+ 2 * T. T[ 1 ] . num* ( a[ i+ 1 ] . y- a[ i] . y) ;
laspos= T. T[ 1 ] . len;
}
printf ( "%lld\n" , ans) ;
return 0 ;
}