upd 20220312:新增了算法过程、模板代码
推荐网页:OI Wiki_扫描线
前铺知识
一、扫描线是什么
- 顾名思义,扫描线是指一条线在图上从下往上扫,并维护一些特定的值。
如图,这几条粉色线把图分成了几个部分。
同样,我们用黄色线又把图分成了另外几个部分。
这样,我们就可以用底 × × ×高来求出每一个部分的面积,并把它们合起来得到这三个矩形的面积并。
我们从左往右扫(或从下往上,总之有一个顺序),若碰到了一个矩形的左边,就把碰到的这条线段用线段树标记一下,并计算面积。比如,这条线段为 [ 1 , 3 ] [1,3] [1,3],就把数组中下标为 1 − 2 1-2 1−2 的元素加 1 1 1,也就是用 1 1 1 号元素代表 [ 1 , 2 ] [1,2] [1,2] 这一段,用 2 2 2 号元素代表 [ 2 , 3 ] [2,3] [2,3] 这一段。
若碰到了一个矩形的右边,则把碰到的边在数组中减去,并计算面积。
设上一次碰到便(加入或减去)的横坐标为 x 1 x_1 x1,现在这条边的横坐标为 x 2 x_2 x2,则待计算的若干个矩形宽(上下边)均为 x 2 − x 1 x_2-x_1 x2−x1。此时再用线段树求出数组中有标记(值不为 0 0 0)的元素个数求出来,就可以计算出这若干个矩形的面积了。
模板:Luogu P5490
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct node
{
long long x,y,yy,fl;
}line[200010];
struct node1
{
long long xl,xr,sum,tag;
}tree[8000010];
long long ans;
int s[200010];
bool cmp(node x,node y)
{
return x.x < y.x;
}
void upd(int x)
{
if (tree[x].tag)
{
tree[x].sum = tree[x].xr - tree[x].xl;
}
else tree[x].sum = tree[x * 2].sum + tree[x * 2 + 1].sum;
}
void build(int l,int r,long long x)
{
tree[x].xl = s[l];
tree[x].xr = s[r];
if (l == r) return ;
if (r - l == 1)
{
return ;
}
int mid = (l + r) / 2;
build(l,mid,x * 2);
build(mid,r,x * 2 + 1);
upd(x);
}
void add(int x,long long ll,long long rr,int y)
{
if (tree[x].xl > rr || tree[x].xr < ll)
{
return ;
}
if (tree[x].xl >= ll && tree[x].xr <= rr)
{
tree[x].tag += y;
upd(x);
return ;
}
if (tree[x * 2].xr >= ll) add(x * 2,ll,min(tree[x * 2].xr,rr),y);
if (tree[x * 2 + 1].xl < rr) add(x * 2 + 1,max(tree[x * 2 + 1].xl,ll),rr,y);
upd(x);
}
int main()
{
cin >> n;
for (int i = 1;i <= n;i ++)
{
int x,y,xx,yy;
cin >> x >> y >> xx >> yy;
line[i] = (node){x,y,yy,1};
line[i + n] = (node){xx,y,yy,-1};
s[i] = y;
s[i + n] = yy;
}
sort(s + 1,s + 2 * n + 1);
sort(line + 1,line + 2 * n + 1,cmp);//按x排序
build(1,2 * n,1);
add(1,line[1].y,line[1].yy,line[1].fl);
for (int i = 2;i <= 2 * n;i ++)
{
ans += (line[i].x - line[i - 1].x) * tree[1].sum;
add(1,line[i].y,line[i].yy,line[i].fl);
}
cout << ans << endl;
}
一点五、离散化
- 听起来很高级,实际上就是以 y y y轴排序。。。