亚特兰蒂斯

求n个矩形的面积并

思路构成:
我们要维护两个两: 一个是区间的覆盖次数 第二个是题目中所要求查询的答案
答案依赖于区间覆盖次数 因此可以考虑到在更新次数的时候更新答案

首先 我们最直观的思路是在线段树上维护两个标记:
cnt[x] 表示这个区间的出现次数
ret[x] 表示所查询的结果
思路经历了如下几个过程:

  1. 需不需要加tag的问题 本来思考着cnt[x]当做tag一块使用,但是这里出现了一个问题: 作为tag 是必须要清零的 但是cnt作为答案不能够清零的 所以我们需要添加tag
  2. cnt的更新问题 本来认为在修改之后cnt不需要更新 但是这样的话 对于子区间修改后的cnt无法更新父节点 因此考虑到\(cnt[x]=min(cnt[x*2],cnt[x*2+1])\)
  3. 最后统计答案啊的问题 在修改时 如果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;
}

注意:

  1. 成对操作的时候可以让区间仅仅是区间 配合上len的实现方式 完美的做到了不打tag 即便是大区间完全被多次操作覆盖了 这个区间cnt仍然是0 len也能正确更新
  2. 要开16倍空间: 因为在l==r的时候访问了x2和x2+1
posted @ 2021-12-28 21:09  __iostream  阅读(18)  评论(0)    收藏  举报