poj1151 Atlantis求矩形面积并(线段树+扫描线)

这里介绍一个并不是很好理解的办法。
首先我们考虑将每个矩形的上下边进行拆解,然后排序进行扫描线。
如果我们令下边设成将对应横坐标区间都 + 1 +1 +1,上边设成对应横坐标区间 − 1 -1 1。那么对于相邻两个边来说,我们就是要求高度差 × \times ×权值大于1的横坐标区间长度。

考虑用线段树来维护这个过程,我们对横坐标进行离散化,然后对于线段树上的每个叶子节点,我们维护的是一个长度,长度是这个横坐标到下一个横坐标的长度。
然后每次对于修改的时候,进行一次区间修改即可 ( 注 意 r 要 − 1 ) (注意r要-1) r1

但是我们这时候会发现一个问题
我们没有办法直接维护一段区间的大于 1 1 1的长度,但是我们可以维护一个最小值的长度,然后用总长度,减去 0 0 0的长度,就能得到了!!!

for (int i=1;i<=cnt;i++) ymh=ymh+(x[i+1]-x[i]);
sum=sum+(ymh-query(1,1,cnt,1,cnt))*(a[i].h-lastans);

对于每一次 q u e r y query query,我们就可以直接询问区间最小值的个数。

db query(int root,int l,int r,int x,int y)
{
	if (x<=l && r<=y) return (f[root].ans)*(f[root].mn==0);
	pushdown(root,l,r);
	int mid =l+r >> 1;
	db ans=0;
	if(x<=mid) ans=ans+query(2*root,l,mid,x,y);
	if (y>mid) ans=ans+query(2*root+1,mid+1,r,x,y);
	return ans;
}

那么这个题大致上就这样解决了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]
#define int long long
#define db double

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 5010;

struct Node{
    int mn,len;
    db ans;
};

struct qq{
  db l,r;
  db h,opt;
};

Node f[4*maxn];
int n,m;
db x[maxn],y[maxn];
int add[4*maxn];
qq a[maxn];

void init()
{
	memset(f,0,sizeof(f));
	memset(a,0,sizeof(a));
	memset(x,0,sizeof(x));
	memset(y,0,sizeof(y));
	memset(add,0,sizeof(add));
}

void up(int root)
{
  f[root].mn=min(f[2*root].mn,f[2*root+1].mn);
  f[root].ans = (f[2*root].mn==f[root].mn)*f[2*root].ans;
  f[root].ans += (f[2*root+1].mn==f[root].mn)*f[2*root+1].ans;
}

void pushdown(int root,int l,int r)
{
	if (add[root])
	{
		add[2*root]+=add[root];
		add[2*root+1]+=add[root];
		f[2*root].mn+=add[root];
		f[2*root+1].mn+=add[root];
		add[root]=0;
    }
}

bool cmp(qq a,qq b)
{
	return a.h<b.h;
}

void build(int root,int l,int r)
{
  if (l==r)
  {
  	 f[root].ans=x[l+1]-x[l];
  	 f[root].mn=0;
  	 return;
  }
  int mid = l+r >> 1;
  build(2*root,l,mid);
  build(2*root+1,mid+1,r);
  up(root);
}

void update(int root,int l,int r,int x,int y,int p)
{
	if (x<=l && r<=y)
	{
		add[root]+=p;
		f[root].mn+=p;
		return;
    }
    pushdown(root,l,r);
    int mid =l+r >> 1;
    if (x<=mid) update(2*root,l,mid,x,y,p);
    if (y>mid) update(2*root+1,mid+1,r,x,y,p);
    up(root);
}

db query(int root,int l,int r,int x,int y)
{
	if (x<=l && r<=y) return (f[root].ans)*(f[root].mn==0);
	pushdown(root,l,r);
	int mid =l+r >> 1;
	db ans=0;
	if(x<=mid) ans=ans+query(2*root,l,mid,x,y);
	if (y>mid) ans=ans+query(2*root+1,mid+1,r,x,y);
	return ans;
}

signed main()
{
  int uu=0;
  while (1)
  {
  	  ++uu;
  	  init();
  	  n=read();
  	  if (n==0) break;
  	  int cnt=0;
  	  int tmp=0;
  	  for (int i=1;i<=n;i++)
  	  {
  	  	 db b,c,d,e;
  	  	 scanf("%lf%lf%lf%lf",&b,&c,&d,&e);
  	  	 a[++tmp].h = c;
  	  	 a[tmp].opt=1;
  	  	 a[tmp].l=b;
  	  	 a[tmp].r=d;
  	  	 a[++tmp].h = e;
  	  	 a[tmp].opt=-1;
  	  	 a[tmp].l=b;
  	  	 a[tmp].r=d;
  	  	 x[++cnt]=b;
  	  	 x[++cnt]=d;
  	  }
  	  sort(a+1,a+1+tmp,cmp);
  	  sort(x+1,x+1+cnt);
  	  int num = cnt;
  	  cnt = unique(x+1,x+1+num) - x-1;
  	  db ymh =0;
	  x[cnt+1]=x[cnt];
  	  for (int i=1;i<=cnt;i++) ymh=ymh+(x[i+1]-x[i]);
	  build(1,1,cnt);
	  //cout<<f[1].ans<<endl;
	  db sum=0;
	  //cout<<ymh<<endl;
	  db lastans=a[1].h;
	  for (int i=1;i<=tmp;i++)
	  {
	     int l = lower_bound(x+1,x+1+cnt,a[i].l)-x;
	     int r = lower_bound(x+1,x+1+cnt,a[i].r)-x-1;
	  	 
	  	 sum=sum+(ymh-query(1,1,cnt,1,cnt))*(a[i].h-lastans);
	  	 lastans=a[i].h;
		 update(1,1,cnt,l,r,a[i].opt);//cout<<sum<<" "<<query(1,1,cnt,1,cnt)<<" "<<l<<" "<<r<<endl;
	  }
	  printf("Test case #%d\n",uu);
        printf("Total explored area: %.2f\n\n",sum); 
	  //cout<<sum<<"\n";
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值