线段树 hdu3642 Get The Treasury

不得不说,这是一题非常经典的体积并。。然而还是debug了2个多小时...

首先思路:按z的大小排序。

然后相当于扫描面一样,,从体积的最下方向上方扫描,遇到这个面

就将对应的两条线加入到set中,或者从set中删除,然后再对set中的所有边,求一次面积并

由于最后求出来的是至少有3个体积叠加的部分的体积。

所以需要维护3个节点,然后push_up会稍微啰嗦一点...

刚开始在set插入线段和删除线段的时候,搞错了线段所在面的编号,,然后一直样例都过不去也是醉了...

总的来说代码比较冗长需要足够细心才能敲对..

#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define root 1,rear-1,1

int const MX = 4e3 + 5;

struct Side {
    int z, x1, y1, x2, y2, d, id;
    bool operator<(const Side &b)const {
        if(z == b.z) return d < b.d;
        return z < b.z;
    }
    Side() {}
    Side(int _z, int _id, int _x1, int _y1, int _x2, int _y2, int _d) {
        z = _z; x1 = _x1; y1 = _y1; x2 = _x2; y2 = _y2; d = _d; id = _id;
    }
} A[MX];

struct Line {
    int y, x1, x2, d, id;
    bool operator<(const Line &b)const {
        if(y == b.y) {
            if(d == b.d) return id < b.id;
            return d < b.d;
        }
        return y < b.y;
    }
    Line() {}
    Line(int _y, int _id, int _d, int _x1, int _x2) {
        y = _y; x1 = _x1; x2 = _x2; d = _d; id = _id;
    }
};

int X[MX], rear;
int S1[MX << 2], S2[MX << 2], S3[MX << 2], cnt[MX << 2];
set<Line>work;

int BS(int x) {
    int L = 1, R = rear, m;
    while(L <= R) {
        m = (L + R) >> 1;
        if(X[m] == x) return m;
        if(X[m] > x) R = m - 1;
        else L = m + 1;
    }
    return -1;
}

void push_up(int l, int r, int rt) {
    if(cnt[rt]) {
        S1[rt] = X[r + 1] - X[l];
        if(cnt[rt] == 1) {
            S2[rt] = S1[rt << 1] + S1[rt << 1 | 1];
            S3[rt] = S2[rt << 1] + S2[rt << 1 | 1];
        } else {
            S2[rt] = X[r + 1] - X[l];
            if(cnt[rt] == 2) {
                S3[rt] = S1[rt << 1] + S1[rt << 1 | 1];
            } else {
                S3[rt] = X[r + 1] - X[l];
            }
        }
    } else if(l == r) S1[rt] = S2[rt] = S3[rt] = 0;
    else {
        S1[rt] = S1[rt << 1] + S1[rt << 1 | 1];
        S2[rt] = S2[rt << 1] + S2[rt << 1 | 1];
        S3[rt] = S3[rt << 1] + S3[rt << 1 | 1];
    }
}

void update(int L, int R, int d, int l, int r, int rt) {
    if(L <= l && r <= R) {
        cnt[rt] += d;
        push_up(l, r, rt);
        return;
    }

    int m = (l + r) >> 1;
    if(L <= m) update(L, R, d, lson);
    if(R > m) update(L, R, d, rson);
    push_up(l, r, rt);
}

LL solve() {
    memset(S1, 0, sizeof(S1));
    memset(S2, 0, sizeof(S2));
    memset(cnt, 0, sizeof(cnt));

    LL ans = 0;
    int last = 0;
    for(set<Line>::iterator it = work.begin(); it != work.end(); it++) {
        ans += (LL)(it->y - last) * S3[1];
        update(BS(it->x1), BS(it->x2) - 1, it->d, root);
        last = it->y;
    }
    return ans;
}

int main() {
    //freopen("input.txt", "r", stdin);
    int T, n, ansk = 0;
    scanf("%d", &T);
    while(T--) {
        rear = 0;
        work.clear();

        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            int x1, y1, z1, x2, y2, z2;
            scanf("%d%d%d%d%d%d", &x1, &y1, &z1, &x2, &y2, &z2);
            A[i] = Side(z1, i, x1, y1, x2, y2, 1);
            A[i + n] = Side(z2, i, x1, y1, x2, y2, -1);
            X[++rear] = x1; X[++rear] = x2;
        }
        sort(A + 1, A + 1 + 2 * n);
        sort(X + 1, X + 1 + rear);
        rear = unique(X + 1, X + 1 + rear) - X - 1;

        LL ans = 0;
        int last = 0;
        printf("Case %d: ", ++ansk);
        for(int i = 1; i <= 2 * n; i++) {
            ans += (LL)(A[i].z - last) * solve();

            if(A[i].d > 0) {
                work.insert(Line(A[i].y1, A[i].id, 1, A[i].x1, A[i].x2));
                work.insert(Line(A[i].y2, A[i].id, -1, A[i].x1, A[i].x2));
            } else {
                work.erase(Line(A[i].y1, A[i].id, 1, A[i].x1, A[i].x2));
                work.erase(Line(A[i].y2, A[i].id, -1, A[i].x1, A[i].x2));
            }
            last = A[i].z;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 17:34:47

线段树 hdu3642 Get The Treasury的相关文章

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 3642 Get The Treasury(线段树)

HDU 3642 Get The Treasury 题目链接 题意:给定一些立方体,求体积重叠超过3次的 思路:由于z坐标只有500,那么就可以枚举z坐标,每次做x,y的面积并即可,用线段树维护 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1005; const int INF = 0x3f3f3f3f; typedef

HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description Jack knows that there is a great underground treasury in a secret region. And he has a special d

数据结构---线段树

线段树 转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树

(转载)线段树模板(来自胡浩大牛)

http://www.notonlysuccess.com/(今天看二叉树,想回来看看,发现大牛博客进不去...) 如果要学,就要好好学.我copy的,如有错,请看http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2134427.html [完全版]线段树 很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku打广告,但是现在我自己都不太好意思去看那篇文章了,觉得当时的代码风格实在是太丑了,很多线段树的初学者

线段树——转

  一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍 2:基本操作(demo用的是查询区间最小值) 线段树的主要操作有: (1):线段树的构造 void build(int node,

线段树(转)

线段树   转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线

线段树题目总结

一.单点更新 1.hdu1166 敌兵布阵:有N个兵营,每个兵营都给出了人数ai(下标从1开始),有四种命令,(1)"Addij",表示第i个营地增加j人.(2)"Sub i j",表示第i个营地减少j人.(3)"Query ij",查询第i个营地到第j个营地的总人数.(4)"End",表示命令结束.解题报告Here. 2.hdu1754 I Hate It:给你N个数,M个操作,操作分两类.(1)"QAB"

线段树总结 (转载 里面有扫描线类 还有NotOnlySuccess线段树大神的地址)

转载自:http://blog.csdn.net/shiqi_614/article/details/8228102 之前做了些线段树相关的题目,开学一段时间后,想着把它整理下,完成了大牛NotOnlySuccess的博文“完全版线段树”里的大部分题目,其博文地址Here,然后也加入了自己做过的一些题目.整理时,更新了之前的代码风格,不过旧的代码仍然保留着. 同样分成四类,不好归到前四类的都分到了其他.树状数组能做,线段树都能做(如果是内存限制例外),所以也有些树状数组的题目,会标示出来,并且放