hdu 3642(线段树+扫描线)

三维扫描线,枚举z寻找相交区间的立方体,然后直接扫描线求xy平面的相交三次及以上面积,乘以z区间求和就可以了

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=1e6+100;
const int maxm=2000+500;
int col[maxn<<2];
int len1[maxn<<2],len2[maxn<<2],sum[maxn<<2];//重合线段分别为1 2 3 的线段长度
int zz[maxm],xx[maxm];
typedef long long ll;
int t,n;
struct Point
{
    int x,y,z;
    void read()
    {
        scanf("%d%d%d",&x,&y,&z);
    }
};
struct graphic
{
    Point a,b;
}gg[maxm];
struct note
{
    int l,r,h;
    int z;
    int ff;
    note() {}
    note(int l,int r,int h,int ff):l(l),r(r),h(h),ff(ff) {}
    bool operator <(const note &p) const
    {
        return h<p.h;
    }
}aa[maxm];
void pushup(int l,int r,int rt)
{
    if(col[rt]>=3)
    {
        sum[rt]=xx[r+1]-xx[l];
        len1[rt]=len2[rt]=0;
    }
    else if(col[rt]==2)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1]+len1[rt<<1]+len1[rt<<1|1]+len2[rt<<1]+len2[rt<<1|1];
        len2[rt]=xx[r+1]-xx[l]-sum[rt];//总长度减去重合次数>=3的部分才是重合次数==2的部分
        len1[rt]=0;
    }
    else if(col[rt]==1)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1]+len2[rt<<1]+len2[rt<<1|1];
        len2[rt]=len1[rt<<1]+len1[rt<<1|1];
        len1[rt]=xx[r+1]-xx[l]-sum[rt]-len2[rt];//总长度减去重合次数>=和==2的部分才是重合次数==1的部分
    }
    else if(l==r)
    {
        sum[rt]=len1[rt]=len2[rt]=0;
    }
    else
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
        len1[rt]=len1[rt<<1]+len1[rt<<1|1];
        len2[rt]=len2[rt<<1]+len2[rt<<1|1];
    }
}
void update(int L,int R,int v,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        col[rt]+=v;
        pushup(l,r,rt);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,v,l,mid,rt<<1);
    if(mid<R) update(L,R,v,mid+1,r,rt<<1|1);
    pushup(l,r,rt);
}
int main()
{
    scanf("%d",&t);
    int Case=0;
    while(t--)
    {
        scanf("%d",&n);
        int cx=0,cz=0;
         for(int i=1;i<=n;i++)
         {
             gg[i].a.read();
             gg[i].b.read();
             zz[cz++]=gg[i].a.z;
             zz[cz++]=gg[i].b.z;
             xx[cx++]=gg[i].a.x;
             xx[cx++]=gg[i].b.x;
         }
         printf("Case %d: ",++Case);
         if(n<3)
         {
             printf("0\n");
             continue;
         }
         sort(zz,zz+cz);
         sort(xx,xx+cx);
         cz=unique(zz,zz+cz)-zz;
         cx=unique(xx,xx+cx)-xx;
         int nn=2000+10;
         ll ans=0;
         for(int j=0;j<cz-1;j++)//离散化z后枚举区间,寻找各个立方体和区间重合的部分
         {
             int cnt=0;
             for(int i=1;i<=n;i++)
             {
                 if(gg[i].a.z<=zz[j]&&gg[i].b.z>zz[j])//枚举的区间是离散化z后的,所以必然小于任意一个立方体的高
                 {
                     aa[++cnt]=note(gg[i].a.x,gg[i].b.x,gg[i].a.y,1);
                     aa[++cnt]=note(gg[i].a.x,gg[i].b.x,gg[i].b.y,-1);
                 }
             }
             sort(aa+1,aa+cnt+1);
             ll sss=0;
             for(int i=1;i<=cnt;i++)
             {
                 int hl=lower_bound(xx,xx+cx,aa[i].l)-xx;
                 int hr=lower_bound(xx,xx+cx,aa[i].r)-xx;
                 sss+=(ll)sum[1]*(ll)(aa[i].h-aa[i-1].h);
                 update(hl,hr-1,aa[i].ff,0,nn,1);
             }
             ans+=sss*(ll)(zz[j+1]-zz[j]);//重合三次后的面积乘以高
         }
         printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Wangwanxiang/p/9441834.html

时间: 2025-01-11 16:14:39

hdu 3642(线段树+扫描线)的相关文章

HDU 3642 线段树+离散化+扫描线

题意:给你N个长方体的左下角和右上角坐标,问你空间中有多少体积是被大于两个不同的立方体覆盖的.x,y~10^6 z~500 考虑到给的z比较小,所以可以直接枚举z,然后跑二维的扫描线就好. 关于处理被不同的线段覆盖三次的问题,可以维护四个信息,cnt,once,twice,more,然后相互推出结果就好. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> #

HDU 5107 线段树扫描线

给出N个点(x,y),每个点有一个高度h 给出M次询问,问在(x,y)范围内第k小的高度是多少,没有输出-1 (k<=10) 线段树扫描线 首先离散化Y坐标,以Y坐标建立线段树 对所有的点和询问进行离线操作,将询问和点按照x,y的大小排序,从左向右,从下向上,对于相同的(x,y)插入点在询问点之前 线段树的每个节点维护10个高度,每次询问[0,mark[i].y]的第mark[i].h高的值即可 #include "stdio.h" #include "string.h

hdu 1255(线段树 扫描线) 覆盖的面积

http://acm.hdu.edu.cn/showproblem.php?pid=1255 典型线段树辅助扫描线,顾名思义扫描线就是相当于yy出一条直线从左到右(也可以从上到下)扫描过去,此时先将所有的横坐标和纵坐标排序 因为是从左到右扫描,那么横坐标应该离散化一下 当扫描线依次扫描的时候,依次扫描到的纵区间在线段树中查找,依据是上边还是下边记录,上边就是-1,下边就是+1, 如果某区间记录值为0的时候,代表没有被覆盖,为1的时候代表覆盖一次,为2代表覆盖两次(不会出现为负数的情况) 最后将依

hdu 1542 线段树+扫描线

啦啦啦~继续学算法 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542 Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7349    Accepted Submission(s): 3231 Problem Description There are several

HDU 5091 线段树扫描线

给出N个点,和一个w*h的矩形 给出N个点的坐标,求该矩形最多可以覆盖多少个点 对每个点point(x,y)右边生成对应的点(x+w,y)值为-1: 纵向建立线段树,从左到右扫描线扫一遍,遇到点则用该点的权值更新区间(y,y+h) #include "stdio.h" #include "string.h" #include "algorithm" using namespace std; struct Mark { int x,y,s; }ma

HDU 1542 线段树+扫描线+离散化

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 8327    Accepted Submission(s): 3627 Problem Description There are several ancient Greek texts that contain descriptions of the fabled is

hdu 1264 线段树+扫描线

说实话  真是水题   题目给的数据太小  都不用离散化      之前用离散化处理一个类似的题 这道题是给你几个矩形    让你求覆盖面积    重复的只算一次 做完之后被坑了        题目说输入左下角和右下角坐标    但是     左上角和右下角也有可能   只要是对角就行    所有得线处理一下 #include<stdio.h> #include<iostream> #include<algorithm> #include<string.h>

HDU 2461 线段树扫描线

给出N个矩形,M次询问 每次询问给出R个,问这R个矩形围成的面积 经典扫面线求面积并,对每次询问的R个点离散化一下 #include "stdio.h" #include "string.h" #include "algorithm" #include "map" using namespace std; map<int,int>mp; struct P { int x1,y1,x2,y2; }p[21]; str

HDU 3642 Get The Treasury 线段树+扫描线

反向标记是错的,要对矩形进行拆分 #include <cstdio> #include <algorithm> #include <cstring> #include <vector> typedef long long LL; using namespace std; #define lson rt << 1,l,mid #define rson rt << 1 | 1,mid + 1,r const int maxn = 5e4

HDU 1828 Picture 线段树+扫描线

题意:给你一些矩形的左上角点的坐标和右下角点的坐标,求周长并 最显而易见的思路就是对于x轴和y轴做两次扫描线,对于负数的坐标进行离散化.每次增加的值是线段变化量的绝对值.具体写法和求面积并的差不多. #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; #define lson rt << 1 , l , m