算法学习——————扫描线
回顾才发现扫描线还没有写过
解决问题:矩形的面积并,周长并问题
思想:利用分割法求矩形的面积
做法:
规定:从下往上扫
- 首先对纵坐标进行排序
问题:范围很大
- 离散化(注意求面积的时候不能用离散化后的坐标)
整体过程:遇下边加边,遇上边减边,切割出的矩形面积就是当前线段长度\(\times\)两次修改间的高度
- 下边权值设为1,上边权值设为1,加入到线段树内
维护信息(暂):区间内的线段长度
问题是,删除一条线段的时候可能区间线段长度不变,因为可能有多条线段有重合
- lazy表示区间被几条线段完整覆盖,tree表示区间内的线段长度,若lazy > 0,tree = 区间长度,否则tree = tree[ls] + tree[rs]
最后一个问题,我也有很多的疑问,举个别人题解里的例子
比如离散化后有四个X,我们一次修改要修改离散化后1-3(原X1-X3),模拟在线段树的过程,我们会分别走到1-2和3-3两个区间,第一个区间没有问题tree = X2-X1,那么第二个区间呢?tree = X3-X3 = 0 ?显然是错误的
- 那么在这里我们的做法就是修改定义,让线段树对应的l-r区间改为l-r+1区间,这样就保证了长度为len的区间能被分成len个单位区间,修改区间l-r的时候只要修改l-r-1就可以了
最后因为询问整个线段树,所以不需要pushdown
顺便在写代码的时候还发现几个问题
-
最后一次不需要修改,因为一定是上边,对答案没有价值,而且没有line[i+1]
-
数组大小
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define O(x) cout<<#x<<" "<<x<<endl;
#define B cout<<"Breakpoint"<<endl;
#define ll long long
using namespace std;
ll read(){
ll x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 4e5+10;
int n;
struct node{
int l,r,high,op;
}line[maxn << 1];
bool cmp(node x,node y){
return x.high < y.high;
}
ll cnt,X[maxn << 1];
ll tree[maxn << 2],lazy[maxn << 2];
ll ls(int x){return x << 1;}
ll rs(int x){return x << 1|1;}
void pushup(int x,int l,int r){
if (lazy[x]) tree[x] = X[r+1]-X[l];
else tree[x] = tree[ls(x)] + tree[rs(x)];
}
void modify(int x,int l,int r,int nl,int nr,int v){
if (nl <= X[l]&&X[r+1] <= nr){
lazy[x] += v;pushup(x,l,r);
return;
}
int mid = (l+r) >> 1;
if (nl <= X[mid]) modify(ls(x),l,mid,nl,nr,v);
if (nr > X[mid+1]) modify(rs(x),mid+1,r,nl,nr,v);
pushup(x,l,r);
}
ll ans;
int main(){
n = read();
for (int i = 1;i <= n;i++){
ll x_1,x_2,y_1,y_2;
x_1 = read(),y_1 = read(),x_2 = read(),y_2 = read();
line[++cnt] = (node){x_1,x_2,y_1,1},X[cnt] = x_1;
line[++cnt] = (node){x_1,x_2,y_2,-1},X[cnt] = x_2;
}
sort(line+1,line+cnt+1,cmp);
sort(X+1,X+cnt+1);
int tot = unique(X+1,X+cnt+1)-X-1;
for (int i = 1;i < cnt;i++){
modify(1,1,tot-1,line[i].l,line[i].r,line[i].op);
ans += tree[1]*(line[i+1].high-line[i].high);
}
cout<<ans<<endl;
return 0;
}
还算简便??怎么会有人说我代码可读性差