bzoj 1845: [Cqoi2005] 三角形面积并 (扫描线+计算几何)

题目描述

传送门

题目大意:给出n个三角形,求它们并的面积。

题解

MD被卡了一下午精度,各种不爽!!!
同样被卡的同学建议使用long double试一试。。。。
我们把三角形的顶点以及所有交点的横坐标离散,那么相邻两列之间的图形要么是梯形要么是三角形,只要统计出两列上的被覆盖区域的长度,就可以用(上底+下底)*高/2来计算。
统计覆盖区域的话,找出所有的区间,左端点排序后扫一遍就可以了。
有一种情况需要注意,就是三角形的一条边平行于y轴,这种情况计算时只能作为上底,或者只能作为下底,特判一下即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 300003
#define eps 1e-12
using namespace std;
const long double inf=1e9;
long double pos[N],ans[N],ans1[N],L[N],R[N];
int n,t,cnt,opt[N];
struct data{
    long double x,y; 
    data(long double X=0,long double Y=0){
        x=X,y=Y;
    }
    bool operator ==(const data &a) {
        return x==a.x&&y==a.y;
    }
}p[N],q[N],h[100];
data operator +(data a,data b){ return data(a.x+b.x,a.y+b.y);}
data operator -(data a,data b){ return data(a.x-b.x,a.y-b.y);}
data operator *(data a,double t){return data(a.x*t,a.y*t);}
data operator /(data a,double t) {return data(a.x/t,a.y/t);}
struct line{
    data a[5],b[5];
    long double mx,mn;
}tr[303];
int dcmp(long double x)
{
    if (fabs(x)<eps) return 0;
    return x>0?1:-1;
}
long double cross(data a,data b)
{
    return a.x*b.y-a.y*b.x;
}
bool check(data a,data b,data c,data d)
{
    long double c1=cross(c-a,b-a); long double c2=cross(d-a,b-a);
    long double d1=cross(a-c,d-c); long double d2=cross(b-c,d-c);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(d1)*dcmp(d2)<0;
}
data glt(data a,data a1,data b,data b1)    
{    
    data v=a1-a; data w=b1-b;    
    data u=a-b;    
    long double t=cross(w,u)/cross(v,w);    
    return a+v*t;    
}    
int cmp(data a,data b)
{
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
long double solve()
{
    sort(q+1,q+t+1,cmp); long double len=0;
    long double l=q[1].x; long double r=q[1].y;
    for (int i=2;i<=t+1;i++){
        if (q[i].x>r||i==t+1) {
            len+=r-l;
            l=q[i].x; r=q[i].y;
        }
        else r=max(r,q[i].y);
    }
    return len;
}
int check(int i,data a,data b,data c,long double mx,long double mn)
{
    int t=0; 
    if (a.x==mx) t++,L[i]=min(L[i],a.y),R[i]=max(R[i],a.y);
    if (b.x==mx) t++,L[i]=min(L[i],b.y),R[i]=max(R[i],b.y);
    if (c.x==mx) t++,L[i]=min(L[i],c.y),R[i]=max(R[i],c.y);
    if (t>=2) return 1;
    t=0; L[i]=inf; R[i]=-inf;
    if (a.x==mn) t++,L[i]=min(L[i],a.y),R[i]=max(R[i],a.y);
    if (b.x==mn) t++,L[i]=min(L[i],b.y),R[i]=max(R[i],b.y);
    if (c.x==mn) t++,L[i]=min(L[i],c.y),R[i]=max(R[i],c.y);
    if (t>=2) return 2;
}
int main()
{
    freopen("a.in","r",stdin);
//  freopen("my.out","w",stdout);
    cnt=0; scanf("%d",&n);
    for (int i=1;i<=n;i++){
      data x,y,z; L[i]=inf; R[i]=-inf;
      cin>>x.x>>x.y;
      cin>>y.x>>y.y;
      cin>>z.x>>z.y;
      tr[i].mx=tr[i].mn=x.x; tr[i].mx=max(y.x,tr[i].mx); tr[i].mx=max(z.x,tr[i].mx);
      tr[i].mn=min(y.x,tr[i].mn); tr[i].mn=min(z.x,tr[i].mn);
      tr[i].a[1]=x; tr[i].b[1]=y;
      tr[i].a[2]=y; tr[i].b[2]=z;
      tr[i].a[3]=z; tr[i].b[3]=x;
      p[++cnt]=x; p[++cnt]=y; p[++cnt]=z;
      opt[i]=check(i,x,y,z,tr[i].mx,tr[i].mn);
    }
    for (int i=1;i<=n;i++)
     for (int k=1;k<=3;k++)
      for (int j=i+1;j<=n;j++)
       for (int l=1;l<=3;l++) 
        if (check(tr[i].a[k],tr[i].b[k],tr[j].a[l],tr[j].b[l]))
         p[++cnt]=glt(tr[i].a[k],tr[i].b[k],tr[j].a[l],tr[j].b[l]);
    for (int i=1;i<=cnt;i++) pos[i]=p[i].x;
    sort(pos+1,pos+cnt+1);
    cnt=unique(pos+1,pos+cnt+1)-pos-1;
    for (int i=1;i<=cnt;i++) {
        data a=data(pos[i],-inf); data b=data(pos[i],inf); t=0;
        for (int j=1;j<=n;j++){
         if (pos[i]<tr[j].mn||pos[i]>tr[j].mx) continue;
         if (opt[j]==2&&!dcmp(tr[j].mn-pos[i])) continue;
         if (opt[j]==1&&!dcmp(tr[j].mx-pos[i])) {
             q[++t].x=L[j]; q[t].y=R[j];
             continue;
         }
         int t1=0; 
         for (int k=1;k<=3;k++) {
            data c=tr[j].a[k]; data d=tr[j].b[k];
            if (check(a,b,c,d)) h[++t1]=glt(a,b,c,d);
            if (!dcmp(c.x-pos[i])) h[++t1]=c;
            if (!dcmp(d.x-pos[i])) h[++t1]=d;
         }
         t1=unique(h+1,h+t1+1)-h-1; 
         if (t1<2) continue;
         q[++t].x=min(h[1].y,h[2].y); q[t].y=max(h[1].y,h[2].y);
        }
        ans[i]=solve();
        t=0;
        for (int j=1;j<=n;j++){
         if (pos[i]<tr[j].mn||pos[i]>tr[j].mx) continue;
         if (opt[j]==1&&!dcmp(tr[j].mx-pos[i])) continue;
         if (opt[j]==2&&!dcmp(tr[j].mn-pos[i])) {
             q[++t].x=L[j]; q[t].y=R[j];
             continue;
         }
         int t1=0; 
         for (int k=1;k<=3;k++) {
            data c=tr[j].a[k]; data d=tr[j].b[k];
            if (check(a,b,c,d)) h[++t1]=glt(a,b,c,d);
            if (!dcmp(c.x-pos[i])) h[++t1]=c;
            if (!dcmp(d.x-pos[i])) h[++t1]=d;
         }
         t1=unique(h+1,h+t1+1)-h-1;
         if (t1<2) continue;
         q[++t].x=min(h[1].y,h[2].y); q[t].y=max(h[1].y,h[2].y);
        }
        ans1[i]=solve();
    }
    long double sum=0;
    for (int i=2;i<=cnt;i++) sum+=(ans[i]+ans1[i-1])*(pos[i]-pos[i-1])/2.0;
    printf("%.2lf\n",(double)sum);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值