前言
一个脱了好久的算法,其实不是很难,只不过之前属实是懒死了,就没去学,上一次模拟赛吃亏了,现在来补一下
解决啥
其实就是二位平面上的面积问题等等,其实好像二位数点也可以做2
正文
首先我们考虑一下如何去暴力的做这道题,是不是大概就是递归之类的
其实我们可以发现,这个题目难在我们有两个维度,那么我们就考虑把一维放入线段树里面去,因为线段树维护的就是区间信息啊。
考虑一下线段树维护啥,其实就是l,r这一段区间里面被矩形覆盖的线段的长度,扫到一个线,去看一看他是入边还是出边,入就这一段全是1,出就去除。之后我们这第二维度是不是就化简了
那么我们去考虑一下如何维护这个线段树上的信息
1。左右儿子如何合并:首先发现假如说这个区间被完全覆盖了,我们拿儿子来维护就会多算or少算,所以我们直接用 r - l赋值就好了,假如说没有的话是不是就拿左右儿子去维护。
2。是否需要懒标记:我们仔细想一下,发现好像并不需要让儿子区间知道自己是否被覆盖,因为我们的线段树维护的是现在扫到的线段长度,相反假如说两条线段,一条覆盖了父节点的全部,一条覆盖了子节点的全部,之后我们取删除覆盖父节点的那个线段,我们会发现可以自动的吧儿子的信息传递到父亲上面,就不用再去麻烦的递归了,
3。懒标记维护啥:标记都没有维护个毛线
离散化
最后就是离散化,这道题的范围太大了,就稍微离散化一下,之后我们发现会有一个问题,对于一个线段树节点维护3,3这个区间,我们发现值就变成了Y[3] - Y[3] ,但是我们要的是Y[3] - Y[2] 怎么办,其实就是把l,r的定义改变一下,一个区间l,r维护的是Y[r+1]-Y[l]这样就好了。
coding time
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n , x , y , x_ , y_ , Y[maxn * 2];
struct line{
int l , r , x;
int mark;
}sea[maxn * 2];
bool cmp(line xx , line yy){
return xx.x < yy.x;
}
#define ls o * 2
#define rs o * 2 + 1
long long sum[maxn * 4] , tag[4 * maxn] , L[4 * maxn] , R[4 * maxn];
//sum表示这个节点维护的长度和
//tag表示这个节点维护的线段被扫到的给完全覆盖几次
//L,R显然就是左右端点,这里是为了方便pushup
void build(int o,int l,int r){
L[o] = l , R[o] = r;
if(l == r) return;
int mid = (l + r) >> 1;
build(ls,l,mid);
build(rs,mid + 1 , r);
}
void pushup(int o){
int a = L[o] , b = R[o];
if(tag[o]) sum[o] = Y[b + 1] - Y[a];//完全被覆盖用左右儿子去更新就有可能会重合
else sum[o] = sum[2*o] + sum[2*o+1];
return;
}
void change(int o,int l,int r,int c,int d,int k){
if(Y[r + 1] <= c || Y[l] >= d) return;//特殊判断一下
if(c <= Y[l] && Y[r + 1] <= d){//注意这里是离散化的l,r
tag[o] += k;
pushup(o); //cout<<Y[l]<<" "<<Y[r+1]<<" "<<c<<" "<<d<<" ";
//cout<<sum[o]<<endl;
return;
}
int mid = (l + r) >> 1;
if(Y[mid] >= c) change(ls,l,mid,c,d,k);
if(Y[mid] < d) change(rs,mid+1,r,c,d,k);
pushup(o);
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d%d%d%d",&x,&y,&x_,&y_);
Y[2 * i - 1] = y , Y[2 * i] = y_;
sea[2 * i - 1] = (line){y , y_ , x , 1};//第一条是加入
sea[2 * i] = (line){y , y_ , x_ , -1}; //去除
}
n = n * 2;
sort(sea + 1, sea + 1 + n , cmp);
sort(Y + 1 , Y + 1 + n);
int tail = unique(Y + 1 , Y + 1 + n) - (Y + 1);
build(1,1,tail - 1);//这里我们用左代表Y[l]有代表Y[r + 1]这样就避免了不更新的状况
long long ans = 0;
for(int i = 1 ; i < n ; i++){
change(1,1,tail - 1,sea[i].l,sea[i].r,sea[i].mark);
ans += sum[1] * (sea[i + 1].x - sea[i].x) * 1ll;
}
cout << ans <<"\n";
return 0;
}
杂题
一P1502 窗口的星星
思路比较清晰的一道题,主要是我们做题时要带脑子,不要直接套板子
每一个星星我们可以把他扩大窗户倍数,之后就课以变成一个矩形,只要这个窗子的右顶点在这个矩形内就欧克,之后就是扫描线维护区间面积max,比较简单,但是还是要注意两个点
1.边界不算,其实我们只用把左边和右边都减去0.5这样就好了,因为星星的坐标是没有浮点数的,
2.线段树上的l,r 其实维护的是l,r+1。这里我们这么做对吗?想一下我们为什么在扫描线的模板上要用r+1去覆盖,是不是因为我们这里怕的是一个节点本来代表的是2,3,而他的l,r确实3,3,所以我们要用这个。这里我们是不是相当于对一个线段覆盖,转化为了对于一堆点的覆盖,不需要知道长度,所以说我们是不是就直接l,r就好了