亚特兰蒂斯
求n个矩形的面积并
思路构成:
我们要维护两个两: 一个是区间的覆盖次数 第二个是题目中所要求查询的答案
答案依赖于区间覆盖次数 因此可以考虑到在更新次数的时候更新答案
首先 我们最直观的思路是在线段树上维护两个标记:
cnt[x] 表示这个区间的出现次数
ret[x] 表示所查询的结果
思路经历了如下几个过程:
- 需不需要加tag的问题 本来思考着cnt[x]当做tag一块使用,但是这里出现了一个问题: 作为tag 是必须要清零的 但是cnt作为答案不能够清零的 所以我们需要添加tag
- cnt的更新问题 本来认为在修改之后cnt不需要更新 但是这样的话 对于子区间修改后的cnt无法更新父节点 因此考虑到\(cnt[x]=min(cnt[x*2],cnt[x*2+1])\)
- 最后统计答案啊的问题 在修改时 如果cnt[x]减到了0 不代表这个区间就被减到了0 因为打了标记 子区间还没有更新 所以我们要pushdown 但是子区间也没有更新 就必须pushdown到底部 这样的话一定会TLE
所以我们必须要继续增加维护信息
我们可以再维护区间cnt的最小值 和cnt最小值长度
如果最小值是0 那么就是总长度减去最小值长度
否则答案就是总长度
第二种思路:
我们发现区间的增加和减去都是成对出现的
每次的减去操作一定建立在原来已经覆盖之上
cnt[x]仅仅代表这个区间 与父节点没有关系
长度直接更新即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=20010;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
struct Node
{
double x,y1,y2,k;
void clean(){ x=y1=y2=k=0;}
}s[N];
bool cmp(Node a,Node b){return a.x<b.x;}
int n,T;
double a[N];
int cnt[N<<3],tag[N<<3];
double ret[N<<3];
void clean()
{
for(int i=1;i<=n;i++)
{
cnt[i]=ret[i]=tag[i]=a[i]=0;
s[i].clean();
}
}
void add(int x,int l,int r,int L,int R,int val)
{
if(L<=l&&R>=r)
{
cnt[x]+=val;
ret[x]= cnt[x]?a[r+1]-a[l]:ret[x*2]+ret[x*2+1];
return;
}
int mid=(l+r)>>1;
if(L<=mid) add(x*2,l,mid,L,R,val);
if(R>mid) add(x*2+1,mid+1,r,L,R,val);
ret[x]= cnt[x]?a[r+1]-a[l]:ret[x*2]+ret[x*2+1];
}
int main()
{
while( (n=read())!=0)
{
clean();
for(int i=1;i<=n;i++)
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
s[i*2-1]=(Node){x1,y1,y2,1};
s[i*2]=(Node){x2,y1,y2,-1};
a[i*2-1]=y1; a[i*2]=y2;
}
sort(a+1,a+2*n+1);
int tot=unique(a+1,a+2*n+1)-(a+1);
for(int i=1;i<=n;i++)
{
s[i*2-1].y1=s[i*2].y1=lower_bound(a+1,a+tot+1,s[i*2].y1)-a;
s[i*2-1].y2=s[i*2].y2=lower_bound(a+1,a+tot+1,s[i*2].y2)-a;
}
sort(s+1,s+2*n+1,cmp);
double ans=0;
for(int i=1;i<=n*2;i++)
{
ans+=ret[1]*(s[i].x-s[i-1].x);
add(1,1,tot,s[i].y1,s[i].y2-1,s[i].k);
}
printf("Test case #%d\n",++T);
printf("Total explored area: %.2lf\n",ans);
puts("");
}
return 0;
}
注意:
- 成对操作的时候可以让区间仅仅是区间 配合上len的实现方式 完美的做到了不打tag 即便是大区间完全被多次操作覆盖了 这个区间cnt仍然是0 len也能正确更新
- 要开16倍空间: 因为在l==r的时候访问了x2和x2+1

浙公网安备 33010602011771号