计算直角坐标系的面积并和面积交(可小数)

Posted starve

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算直角坐标系的面积并和面积交(可小数)相关的知识,希望对你有一定的参考价值。

面积并

3个要素:1、离散化,因为坐标可以是浮点数,有些题可能距离很长

      2、扫描线,将每个矩形的俩条平行与x轴的俩条边存到数组里,标记为上边和下边,每次扫描到下边的时候,就将这一段统计起来,扫描到下边的时候就将之前的统计去掉;

      3、线段树,管理矩形的这些边在x轴方向上的有效距离,实际操作就把这些边一段一段地加到线段树上;

注意:线段树的每一个叶子节点点比如tree[L]维护的是的是区间(x[L],x[L+1]),线段树的其他节点例如(L,R)维护的是区间(x[L],x[R+1]);

cnt是记录当前区间有无被覆盖,要是扫描到下边界的话,这个合法区间的cnt就++,扫描到上边界的话就--;

题:http://acm.hdu.edu.cn/showproblem.php?pid=1542

技术图片
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pb push_back
const int M=205;
struct node{
    double l,r,h;
    int flag;
    node(double ll=0,double rr=0,double hh=0,int fl=0):l(ll),r(rr),h(hh),flag(fl){}
    bool operator <(const node &b)const{
        return h<b.h;
    }
}a[M];
struct TREE{
    double sum;
    int cnt;
}tree[M<<2];
double lisan[M];
void up(int root,int l,int r){
    if(tree[root].cnt)
        tree[root].sum=lisan[r+1]-lisan[l];
    else if(l==r)
        tree[root].sum=0;
    else
        tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
void update(int L,int R,int v,int root,int l,int r){
    if(L<=l&&r<=R){
        tree[root].cnt+=v;
        up(root,l,r);
        return ;
    }
    int midd=(l+r)>>1;
    if(L<=midd)
        update(L,R,v,root<<1,l,midd);
    if(R>midd)
        update(L,R,v,root<<1|1,midd+1,r);
    up(root,l,r);///上传到tree【1】 
}
int main(){
    int t=1,n;
    while(scanf("%d",&n)==1&&n){
        for(int i=0;i<=4*n;i++)
            tree[i].cnt=0,tree[i].sum=0;
        for(int i=1;i<=n;i++){
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            lisan[i]=x1;
            lisan[i+n]=x2;
            a[i]=node(x1,x2,y1,1);
            a[i+n]=node(x1,x2,y2,-1);
        }
         
        sort(lisan+1,lisan+1+2*n);
        sort(a+1,a+1+2*n);
        
        int m=unique(lisan+1,lisan+1+2*n)-lisan-1;
        double ans=0;
        for(int i=1;i<2*n;i++){
            int l=lower_bound(lisan+1,lisan+1+m,a[i].l)-lisan;
            int r=lower_bound(lisan+1,lisan+1+m,a[i].r)-lisan;
            if(l<r)///每个节点控制的是区间(lisan[i],lisan[i+1]) 
            ///区间[lisan[l],lisan[r+1]]对应的原本区间即为(l,r-1); 
                update(l,r-1,a[i].flag,1,1,m);
            ans+=(a[i+1].h-a[i].h)*tree[1].sum;
        }
        printf("Test case #%d
",t++);
        printf("Total explored area: %.2f

",ans);
    }
    return 0;
}
View Code

 

面积交:

分析:实际上这个问题就是问被覆盖俩次及以上的面积是多少,所以我们只要再原基础上再记录一个覆盖俩次的标记长度two,接下来就是分析怎么更新这个two;

   要明确一点,就是cnt是实际有意义的,不想lazy值那样是传递的,所以分类讨论一下:

   1、当cnt为0时,表示当前区间没有被完全覆盖,那么覆盖一次和覆盖俩次的长度只能由他们的儿子决定;

   2、当cnt为1时,表示当前区间被完全覆盖过一次,那么覆盖一次的长度就等于x[R+1]-x[L],而覆盖俩次及以上的部分时完全覆盖一次和这个区间儿子覆盖部分的交集,所以只要算儿子覆盖一次带来的贡献即可;

   3,当cnt为2时,表示当前区间被完全覆盖过俩次,覆盖一次和覆盖俩次的长度就为覆盖一次和覆盖俩次的长度;

题:http://acm.hdu.edu.cn/showproblem.php?pid=1255

技术图片
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=2e3+3;
struct node{
    double l,r,h;
    int flag;
    node(double ll=0,double rr=0,double hh=0,int fl=0):l(ll),r(rr),h(hh),flag(fl){}
    bool operator <(const node &b)const{
        return h<b.h;
    }
}a[M];
struct TREE{
    double two,one;
    int cnt;
}tree[M<<2];
double lisan[M];
void up(int root,int l,int r){
    if(tree[root].cnt>=2)
        tree[root].two=tree[root].one=lisan[r+1]-lisan[l];
    else if(tree[root].cnt==1){
        tree[root].one=lisan[r+1]-lisan[l];
        if(l==r)
            tree[root].two=0;
        else
            tree[root].two=tree[root<<1].one+tree[root<<1|1].one;
    } 
    else{
        if(l==r)
            tree[root].one=tree[root].two=0;
        else{
            tree[root].one=tree[root<<1].one+tree[root<<1|1].one; 
            tree[root].two=tree[root<<1].two+tree[root<<1|1].two;
        }
        
    }
}
void update(int L,int R,int v,int root,int l,int r){
    if(L<=l&&r<=R){
        tree[root].cnt+=v;
        up(root,l,r);
        return ;
    }
    int midd=(l+r)>>1;
    if(L<=midd)
        update(L,R,v,root<<1,l,midd);
    if(R>midd)
        update(L,R,v,root<<1|1,midd+1,r);
    up(root,l,r);///上传到tree【1】 
}
int main(){
    int t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
    
        for(int i=1;i<=n;i++){
            double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            lisan[i]=x1;
            lisan[i+n]=x2;
            a[i]=node(x1,x2,y1,1);
            a[i+n]=node(x1,x2,y2,-1);
        }
        n<<=1; 
        sort(lisan+1,lisan+1+n);
        sort(a+1,a+1+n);
        memset(tree,0,sizeof(tree));
        int m=unique(lisan+1,lisan+1+n)-lisan-1;
        double ans=0;
        for(int i=1;i<n;i++){
            int l=lower_bound(lisan+1,lisan+1+m,a[i].l)-lisan;
            int r=lower_bound(lisan+1,lisan+1+m,a[i].r)-lisan;
            if(l<r)///每个节点控制的是区间(lisan[i],lisan[i+1]) 
            ///区间[lisan[l],lisan[r+1]]对应的原本区间即为(l,r-1); 
                update(l,r-1,a[i].flag,1,1,m);
            ans+=(a[i+1].h-a[i].h)*tree[1].two;
        }
        printf("%.2f
",ans);
    }
    return 0;
}
View Code

 

以上是关于计算直角坐标系的面积并和面积交(可小数)的主要内容,如果未能解决你的问题,请参考以下文章

蓝桥杯练习 矩形面积交(计算几何)

蓝桥杯_基础练习《矩形面积交---26》

基础练习 矩形面积交

矩形面积交[蓝桥杯]

蓝桥杯_基础训练_矩阵面积交

XDU_1064:Desolator in RA2