题目:
解题思路:
这题的思路和 【ACWing】247. 亚特兰蒂斯 的差不多,只不过线段树中需要多存储一个变量val2,记录区间内被覆盖2次及以上的线段的长度,在维护的时候,需要根据 cover 的值,分类讨论。
当 cover=0 的时候,代表该区间没有被全部覆盖过,那么该区间的 val1,val2 的长度分别是各自左子区间和右子区间长度之和;
当 cover>=2 的时候,代表该区间至少被完全覆盖了两次,那么 val1,val2 的长度都等于该区间的长度;
当 cover=1 的时候,代表该区间仅被完全覆盖了1次,那么 val1 等于该区间的长度,val2 等于左子区间和右子区间val1 之和(因为已知该区间被完全覆盖一次了,但是有些地方并没有被覆盖第二次,所以需要计算子区间内覆盖一次的长度即可,加在一起,这些长度就是覆盖了两次哦,自己画一下图,理解一下就行了)
代码:
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e4 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
int n;
double ans;
struct line{
double x, y1, y2;
int state;
bool operator<(line b) const {
return x < b.x;
}
};
double ls[MAXN];//去重排序后的y坐标
double alls[MAXN];//所有y坐标
int lscnt;
int ask(double x){
return lower_bound(ls+1, ls+1+lscnt, x) - ls;
}
struct node1{
int l, r;//线段的编号
double val,val2;//该区间覆盖线段的长度
int cnt;//被全部覆盖的次数
}tree[MAXN << 2];
void pushup(int rt){
if(tree[rt].cnt>=2)
{
tree[rt].val = tree[rt].val2 = ls[tree[rt].r+1] - ls[tree[rt].l];
}
else if(tree[rt].cnt==1)
{
tree[rt].val = ls[tree[rt].r+1] - ls[tree[rt].l];
tree[rt].val2 = tree[rt << 1].val + tree[rt << 1 | 1].val;
//注意:cnt=1表示这个父区间整体已经被覆盖了一次,那么覆盖两次的区间长度应该是子区间覆盖1次之和
}
else
{
tree[rt].val = tree[rt << 1].val + tree[rt << 1 | 1].val;
tree[rt].val2 = tree[rt << 1].val2 + tree[rt << 1 | 1].val2;
}
}
void build(int l, int r, int rt){
tree[rt].l = l;
tree[rt].r = r;
tree[rt].val=tree[rt].val2 = tree[rt].cnt = 0;
if(l == r){
return ;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m+1, r, rt << 1 | 1);
}
void update(int L, int R, int state, int rt){
if(L <= tree[rt].l && tree[rt].r <= R){
tree[rt].cnt += state;
pushup(rt);
return ;
}
int m = (tree[rt].l + tree[rt].r) >> 1;
if(L <= m)
update(L, R, state, rt << 1);
if(m < R)
update(L, R, state, rt << 1 | 1);
pushup(rt);
}
void solve(){
vector<line> v;
int cnt=0;
for(int i = 1; i <= n; i++){
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
v.pb(line{x1, y1, y2, 1});
v.pb(line{x2, y1, y2, -1});
alls[++cnt] = y1;
alls[++cnt] = y2;
}
sort(alls+1, alls+1+cnt);
lscnt = 0;
alls[0] = -1;
for(int i = 1; i <= cnt; i++)
if(alls[i] != alls[i-1])
ls[++lscnt] = alls[i];
sort(v.begin(), v.end());
build(1, lscnt-1, 1);
ans = 0;
update(ask(v[0].y1), ask(v[0].y2)-1, v[0].state, 1);
for(int i = 1; i < 2*n; i++){
ans += (v[i].x - v[i-1].x) * tree[1].val2;
update(ask(v[i].y1), ask(v[i].y2)-1, v[i].state, 1);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
qc;
scanf("%d",&n);
solve();
printf("%.2f\n",ans);
memset(alls,0,sizeof(alls));
memset(ls,0,sizeof(ls));
memset(tree,0,sizeof(tree));
}
return 0;
}
碎碎念:
这题是覆盖两次以上的面积之和,相信三次,四次…大家应该都可以想到做法了吧