[USACO07OPEN]城市的地平线City Horizon

本文介绍了USACO竞赛中的一道题目——City Horizon,涉及城市建筑的轮廓面积计算。问题转化为对数列的区间修改操作,通过离线处理和动态开点的方法解决数据范围大的问题。

题目描述

Farmer John has taken his cows on a trip to the city! As the sun sets, the cows gaze at the city horizon and observe the beautiful silhouettes formed by the rectangular buildings.

The entire horizon is represented by a number line with NN (1N40,000) buildings. Building i’s silhouette has a base that spans locations AiAi through BiBi along the horizon (1Ai<Bi1,000,000,0001≤Ai<Bi≤1,000,000,000) and has height HiHi (1Hi1,000,000,0001≤Hi≤1,000,000,000). Determine the area, in square units, of the aggregate silhouette formed by all NN buildings.

题目大意

有一个数列,初始值均为0,进行N次操作,每次将数列[ai,bi)这个区间中所有比HiHi小的数改为HiHi,求NN次操作后数列中所有元素的和。

Solution

看到这道题第一眼就是线段树,但是发现这个在线区间修改真的是很难维护,如果当前的Hi比区间所有的数都大可以直接打标记,但是如果区间有的数比HiHi大就当场去世了。
于是思考离线,发现离线可以完美地解决这个问题,我们把所有修改按HiHi的值从小到大依次排序,这样就可以保证当前的HiHi不会小于区间所有的数,这样就可以直接打标记了。
由于数据范围很大,需要进行离散化,不过我这么懒,当然是动态开点啦!

#include<cstdio>
#include<algorithm>

const int maxn = 40007;
const int INF = 1e9 + 7;

typedef long long ll;

class SegmentTree {
    private :
        struct Node {
            Node *leftchild, *rightchild;
            int push, left, right;
            ll sum;

            Node (int left, int right) :
                left(left),
                right(right),
                leftchild(NULL),
                rightchild(NULL),
                sum(0),
                push(0) {}
        };
        Node *root;

        void AddVal(Node *now, int l, int r, int val) {
            if (!now || now->left > r || now->right < l) {
                return;
            }
            if (now->left >= l && now->right <= r) {
                now->push = val;
                now->sum = (ll)val * (now->right - now->left + 1);
                return;
            }
            PushDown(now);
            int mid = now->left + now->right >> 1;
            if (l > mid) {
                if (!now->rightchild) {
                    now->rightchild = new Node(mid + 1, now->right);
                }
                AddVal(now->rightchild, l, r, val);
            } else if (r <= mid) {
                if (!now->leftchild) {
                    now->leftchild = new Node(now->left, mid);
                }
                AddVal(now->leftchild, l, r, val);
            } else {
                if (!now->leftchild) {
                    now->leftchild = new Node(now->left, mid);
                }
                if (!now->rightchild) {
                    now->rightchild = new Node(mid + 1, now->right);
                }
                AddVal(now->rightchild, l, r, val);
                AddVal(now->leftchild, l, r, val);
            }
            Update(now);
        }

        void PushDown(Node *now) {
            if (now->left == now->right) {
                return;
            }
            if (now->push) {
                int mid = now->left + now->right >> 1;
                if (!now->leftchild) {
                    now->leftchild = new Node(now->left, mid);
                }
                now->leftchild->push = now->push;
                now->leftchild->sum = (ll)now->push * (now->leftchild->right - now->leftchild->left + 1);
                if (!now->rightchild) {
                    now->rightchild = new Node(mid + 1, now->right);
                }
                now->rightchild->push = now->push;
                now->rightchild->sum = (ll)now->push * (now->rightchild->right - now->rightchild->left + 1);
                now->push = 0;
            }
        }

        void Update(Node *now) {
            now->sum = 0;
            if (now->leftchild) {
                now->sum += now->leftchild->sum;
            }
            if (now->rightchild) {
                now->sum += now->rightchild->sum;
            }
        }

    public :
        void Init(int num) {
            root = new Node(1, num);
        }

        void AddVal(int l, int r, int val) {
            AddVal(root, l, r, val);
        }

        ll Sum() {
            return root->sum;
        }
};

class Solution {
    private :
        SegmentTree tree;
        int n, l[maxn], r[maxn], val[maxn], maxx;

        struct Ques {
            int l, r, w;

            Ques(int l = 0, int r = 0, int w = 0) :
                l(l),
                r(r),
                w(w) {}

            bool operator < (const Ques &a) const {
                return this->w < a.w;
            }
        };
        Ques q[maxn];

    public :
        Solution () {
            Get();
            Solve();
        }

        void Get() {
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) {
                scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].w);
                maxx = std :: max(maxx, q[i].r);
            }
        }

        void Solve() {
            tree.Init(maxx);
            std :: sort(q + 1, q + 1 + n);
            for (int i = 1; i <= n; i++) {
                tree.AddVal(q[i].l, q[i].r - 1, q[i].w);
            }
            printf("%lld\n", tree.Sum());
        }
};
Solution sol;

int main() {}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值