线段树扫描线模板

HDU 1542 
HDU 1255
HDU 3642

平面面积并
题目链接
超级棒的题解

#include<bits/stdc++.h>
using namespace std;
const int N=210;
int cnt[N<<2];double sum[N<<2],X[N];
struct seg{
 double h,l,r;int s;
 seg(){
 }
 seg(double a,double b,double c,int s):l(a),r(b),h(c),s(s){
 }
 bool operator <(const seg &x) const 
 {
  return h<x.h ;
 }//高度从下往上 
}ss[N];
void up(int rt,int l,int r)
{
 if(cnt[rt]) sum[rt]=X[r+1]-X[l];//如果依然采用sum相加 会漏掉mid --mid+1这个一区间 
 else if(l==r) sum[rt]=0;
 else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int c,int l,int r,int rt)
{
 if(L<=l&&R>=r) 
 {
  cnt[rt]+=c;
  up(rt,l,r);
  return ; 
 }
 int m=(l+r)>>1;
 if(L<=m) update(L,R,c,l,m,rt<<1);
 if(m<R) update(L,R,c,m+1,r,rt<<1|1);
 up(rt,l,r);
}
int main()
{
 int cas=1;int n ;
 while(~scanf("%d",&n),n)
 {
  int m=0 ;
  while(n--)
  {
   double a,b,c,d;
   scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
   X[m]=a;ss[m++]=seg(a,c,b,1);//左端点,右端点,高度,上下底边 
   X[m]=c;ss[m++]=seg(a,c,d,-1);
  }
  sort(X,X+m);
  sort(ss,ss+m);
  int k=unique (X,X+m)-X;
  memset(cnt,0,sizeof(cnt));
  memset(sum,0,sizeof(sum));
  double ret=0;
  for(int i=0;i<m-1;i++)
  {
   int l=lower_bound(X,X+k,ss[i].l )-X;
   int r=lower_bound(X,X+k,ss[i].r )-X-1;//!!!注意考虑右端点要-1
   update(l,r,ss[i].s ,0,k-1,1);//更新底边长 
   ret+=sum[1]*(ss[i+1].h -ss[i].h ); 
  }
  printf("Test case #%d\n",cas++);
  printf("Total explored area: %.2f\n\n",ret);
 }
 } 

平面面积的交集
LINK
%%

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
double sum[N<<3],one[N<<3],x[N];int cnt[N<<3];//注意空间开大8倍 
//题目要求被覆盖两次及以上的平面的面积 同之前的面积并比较我们需要收集的sum必须是该区间内覆盖次数大于2 的线段长度 
struct node
{
 double l,r,h;int s;
 node(){
 } 
 node(double a,double b,double c,int d) :l(a),r(b),h(c),s(d){
 }
 bool operator <(const node &x ) const 
 {
 
  return h<x.h ; 
 }
}seg[N];
void up(int rt,int l,int r)
{
 if(cnt[rt]>=2) //cnt表示区间被覆盖的次数  
 {
  sum[rt]=x[r+1]-x[l];//区间覆盖次数大于2  
  one[rt]=0;//one 表示该区间中被覆盖一次的长度 
 } 
 if(cnt[rt]==1)
 {
  sum[rt]=sum[rt<<1]+sum[rt<<1|1]+one[rt<<1]+one[rt<<1|1];
  //区间覆盖次数为1 区间中被覆盖两次以上的长度就是之前 被覆盖两次以上的长度+之前被覆盖一次的长度(因为之前覆盖了一次加上现在覆盖次数就是两次了 
  one[rt]=x[r+1]-x[l]-sum[rt];//等于完全覆盖-被覆盖两次以上的区间长度 
 }
 if(cnt[rt]==0)//区间目前的覆盖次数是0 
 {
  sum[rt]=sum[rt<<1|1]+sum[rt<<1];//现在该区间被覆盖两次的长度等于之前被覆盖两次的长度 
  one[rt]=one[rt<<1]+one[rt<<1|1];//同 
 }
}
void update(int rt,int L,int R,int l,int r,int v)
{
 if(L<=l&&R>=r)
 {
  cnt[rt]+=v;
  up(rt,l,r);return ;
 }
 int mid=(l+r)>>1;
 if(L<=mid) update(rt<<1,L,R,l,mid,v);
 if(R>mid) update(rt<<1|1,L,R,mid+1,r,v);
 up(rt,l,r);
} 
int main()
{
 int tt;scanf("%d",&tt);
 while(tt--)
 {
  int n,m=0;
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  {
   double a,b,c,d;
   scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
   x[m]=a;
   seg[m++]=node(a,c,b,1);
   x[m]=c;
   seg[m++]=node(a,c,d,-1);
  }
  sort(x,x+m);
  sort(seg,seg+m);
  memset(sum,0,sizeof(sum));
  memset(cnt,0,sizeof(cnt));
  memset(one,0,sizeof(one));
  int k=unique(x,x+m)-x;double res=0;
  for(int i=0;i<m-1;i++)
  {
   int l=lower_bound(x,x+k,seg[i].l)-x;
   int r=lower_bound(x,x+k,seg[i].r)-x-1;
   update(1,l,r,0,k-1,seg[i].s );
   res+=sum[1]*(seg[i+1].h -seg[i].h);
  }
  printf("%.2lf\n",res);
 }
}

扫描线算法求三维空间重叠三次及以上部分的体积
LINK

%%

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
typedef long long ll;
int x[N],z[N];
int xx,zz,n;int cas=0;
int cnt[N<<3],one[N<<3],two[N<<3],sum[N<<3];
//题目要求求空间中重叠超过三次的部分的体积
//idea:把x z 离散化求 面积(重叠超过三次的部分)乘以高度 
//cnt表示某一个区间内的覆盖次数
//one表示这个区间被覆盖了一次的线段的长度
//two 表示这个区间中被覆盖两次的线段的长度
//sum表示这个区间中被覆盖三次的长度 
struct node
{
 int l,r,h,s;
 node(){
 }
 node (int l,int r,int h,int s):l(l),r(r),h(h),s(s){
 }
 operator <(const node &x) const 
 {
  return h<x.h;
 }
}seg[N];
struct po
{
 int x,y,z;
 void read()
 {
  scanf("%d%d%d",&x,&y,&z);
 }
};
struct cube{
 po a,b;
}A[N];
void up(int rt,int l,int r)//想法的其实和上面第二题很像
{
 if(cnt[rt]>=3)
 {
  sum[rt]=x[r+1]-x[l];
  two[rt]=0;
  one[rt]=0;
  return ;
 }
 if(cnt[rt]==2)
 {
  sum[rt]=sum[rt<<1|1]+sum[rt<<1]+one[rt<<1|1]+one[rt<<1]+two[rt<<1|1]+two[rt<<1];
  one[rt]=0;
  two[rt]=x[r+1]-x[l]-sum[rt];
  return ;
 }
 if(cnt[rt]==1)
 {
  sum[rt]=sum[rt<<1|1]+sum[rt<<1]+two[rt<<1|1]+two[rt<<1];
  two[rt]=one[rt<<1|1]+one[rt<<1];
  one[rt]=x[r+1]-x[l]-sum[rt]-two[rt];
  return ;
 }
 if(cnt[rt]==0)
 {
  sum[rt]=sum[rt<<1]+sum[rt<<1|1];
  two[rt]=two[rt<<1]+two[rt<<1|1];
  one[rt]=one [rt<<1]+one[rt<<1|1];
  return ;
 }
}
void update(int rt,int l,int r,int L,int R,int v)
{
 if(L<=l&&R>=r)
 {
  cnt[rt]+=v;
  up(rt,l,r);
  return ;
 }
 int mid=(l+r)>>1;
 if(L<=mid) update(rt<<1,l,mid,L,R,v);
 if(R>mid) update(rt<<1|1,mid+1,r,L,R,v);
 up(rt,l,r);
}
void deal()
{
 sort(x,x+xx);
 sort(z,z+zz);
 xx=unique(x,x+xx)-x;
 zz=unique(z,z+zz)-z;
 ll ans=0; 
 for(int i=0;i<zz-1;i++)
 {
  int tot=0;
  for(int j=1;j<=n;j++)
  {
   if(A[j].a.z<=z[i]&&A[j].b.z>z[i])
   {
    int x1=A[j].a.x,x2=A[j].b.x;
    int y1=A[j].a.y,y2=A[j].b.y;
    seg[tot++]=node(x1,x2,y1,1);
    seg[tot++]=node(x1,x2,y2,-1);
   }
  }
  memset(cnt,0,sizeof(cnt));
  memset(one,0,sizeof(one));
  memset(two,0,sizeof(two));
  memset(sum,0,sizeof(sum));
  sort(seg,seg+tot);
  ll s=0;
  for(int j=0;j<tot-1;j++)
  {
   int l=lower_bound(x,x+xx,seg[j].l )-x;
   int r=lower_bound(x,x+xx,seg[j].r )-x-1;
   update(1,0,xx-1,l,r,seg[j].s ) ;
   s+=(ll)sum[1]*(seg[j+1].h-seg[j].h );
  }
  ans+=s*(z[i+1]-z[i]);
 }
 printf("Case %d: %lld\n",++cas,ans);
}
int main()
{
 int tt;scanf("%d",&tt);
 while(tt--)
 {
  scanf("%d",&n);
  xx=0;
  zz=0;
  for(int i=1;i<=n;i++)
  {
   A[i].a.read();
   A[i].b.read();
   x[xx++]=A[i].a.x;
   x[xx++]=A[i].b.x;
   z[zz++]=A[i].a.z;
   z[zz++]=A[i].b.z;
  }
  if(n<3)
  {
   printf("Case %d: 0\n",++cas);
  }else 
  {
   deal();
  }
 }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值