扫描线
原理比较易懂
参考:
[1] 一文读懂扫描线算法
面积并
面积并只需要选择x或y其一做扫描线.每次扫描更新线段树,将扫描线长度 * 两次扫描线距离得到子矩形面积,累加统计答案.
/* 扫描线算法 */
/*
* 前置知识: 线段树,离散化
* 矩形面积并
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
struct SL{
ll x;
ll up,down;
int kind;
SL(){};
SL(ll x,ll up,ll down,int kind)
:x(x),up(up),down(down),kind(kind){}
bool operator <(const SL&m)const{
return x < m.x;
}
};
SL line[N<<1];
int cover[N<<3]; // cover[i]表示区间[l,r]的全部结点都被覆盖了cover[i]次; cover[i] >= 0
ll tree[N<<3];
// 通常我们建立N<<2的线段树,但是对于扫描线建树,n个矩形会构造出2n条扫描线,因此需要2N<<2即N<<3的空间
// tree[i] 表示[l,r]区间被覆盖的总长度
int len = 0;
ll discret[N<<1];
void push_up(int l,int r,int rt){
if(cover[rt]){
tree[rt] = discret[r]-discret[l];
}else{
if(l+1==r){ // 到达了叶结点,而叶结点的cover为0,说明这个叶结点没被扫描线覆盖
tree[rt] = 0;
}else{
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
}
}
void update(int l,int r,int rt,int ul,int ur,int degree){
if(ul <= l && ur >= r){
cover[rt] += degree;
push_up(l,r,rt);
}else{
if(l+1==r){ // 运行到这里,说明这是一个不需要更新的叶结点
return;
}
int mid = l+r>>1;
if(ul <= mid){
update(l,mid,rt<<1,ul,ur,degree);
}
if(ur > mid){
update(mid,r,rt<<1|1,ul,ur,degree);
}
push_up(l,r,rt);
}
}
int main(){
int n;
ll xa,xb,ya,yb;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%lld%lld%lld%lld",&xa,&ya,&xb,&yb);
line[++len] = SL(xa,yb,ya,1);
discret[len] = ya;
line[++len] = SL(xb,yb,ya,-1);
discret[len] = yb;
}
sort(discret+1,discret+1+len);
sort(line+1,line+1+len);
int idlabels = unique(discret+1,discret+1+len)-(discret+1);
ll ans = 0;
for(int i = 1; i <= len; i++){
ans += tree[1] * (line[i].x-line[i-1].x);
int ul = lower_bound(discret+1,discret+1+idlabels,line[i].down)-discret;
int ur = lower_bound(discret+1,discret+1+idlabels,line[i].up)-discret;
update(1,idlabels,1,ul,ur,line[i].kind);
}
printf("%lld",ans);
system("pause");
return 0;
}
周长并
周长并需要在x,y方向做两次扫描线. 对于每次线段树的更新,记录扫描线长度的变化量的绝对值.累加统计答案.
注意求周长并,对于距离差值为0的多根扫描线,需要先计算入边,再计算出边.具体可以看这篇题解
#include <cstdio>
#include <algorithm>
using namespace std;
#define MIN(a,b) (a<b?a:b)
#define FABS(x) (x>0?x:-(x))
typedef long long ll;
const int N = 5000+5;
struct data{
ll xa,xb,ya,yb;
};
struct SL{
ll pos,upper,lower;
int kind;
SL(){};
SL(ll a,ll b,ll c,int d):pos(a),upper(b),lower(c),kind(d){}
bool operator<(const SL&m)const{
if(pos != m.pos)
return pos < m.pos;
else return kind > m.kind;
}
};
ll ans;
data arr[N];
int treeSize;
int slSize;
ll tree[N<<3];
ll cover[N<<3];
ll orderPos[N<<2];
SL line[N<<2];
void push_up(int left,int right,int rt){
if(cover[rt]){
tree[rt] = orderPos[right] - orderPos[left];
}else{
if(left+1==right){
tree[rt] = 0;
}else{
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
}
}
void build(int l,int r,int rt){
tree[rt] = 0;
cover[rt] = 0;
if(l+1==r){
return;
}else{
int mid = l+r>>1;
build(l,mid,rt<<1);
build(mid,r,rt<<1|1);
}
}
void clear(){
build(1,treeSize,1);
treeSize = slSize = 0;
}
void update(int l,int r,int rt,int ul,int ur,int kind){
if(ul <= l && ur >= r){
cover[rt] += kind;
push_up(l,r,rt);
}else{
int mid = l+r>>1;
if(l+1==r){
return;
}
if(ul <= mid){
update(l,mid,rt<<1,ul,ur,kind);
}
if(ur > mid){
update(mid,r,rt<<1|1,ul,ur,kind);
}
push_up(l,r,rt);
}
}
void sweep(){
sort(orderPos+1,orderPos+1+slSize);
sort(line+1,line+1+slSize);
treeSize = unique(orderPos+1,orderPos+1+slSize) - (orderPos+1);
for(int i = 1; i <= slSize; i++){
int temp = tree[1];
int ul = lower_bound(orderPos+1,orderPos+1+treeSize,line[i].lower) - orderPos;
int ur = lower_bound(orderPos+1,orderPos+1+treeSize,line[i].upper) - orderPos;
update(1,treeSize,1,ul,ur,line[i].kind);
ans += FABS(tree[1]-temp);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
data &p = arr[i];
scanf("%lld%lld%lld%lld",&p.xa,&p.ya,&p.xb,&p.yb);
}
for(int i = 1; i <= n; i++){
data &p = arr[i];
orderPos[++slSize] = p.ya;
line[slSize].pos = p.xa;
line[slSize].lower = p.ya;
line[slSize].upper = p.yb;
line[slSize].kind = 1;
orderPos[++slSize] = p.yb;
line[slSize].pos = p.xb;
line[slSize].lower = p.ya;
line[slSize].upper = p.yb;
line[slSize].kind = -1;
}
sweep();
clear();
for(int i = 1; i <= n; i++){
data &p = arr[i];
orderPos[++slSize] = p.xa;
line[slSize].pos = p.ya;
line[slSize].lower = p.xa;
line[slSize].upper = p.xb;
line[slSize].kind = 1;
orderPos[++slSize] = p.xb;
line[slSize].pos = p.yb;
line[slSize].lower = p.xa;
line[slSize].upper = p.xb;
line[slSize].kind = -1;
}
sweep();
printf("%lld",ans);
// system("pause");
return 0;
}