题目描述
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 () buildings. Building i’s silhouette has a base that spans locations AiAi through BiBi along the horizon (1≤Ai<Bi≤1,000,000,0001≤Ai<Bi≤1,000,000,000) and has height HiHi (1≤Hi≤1,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次操作,每次将数列这个区间中所有比HiHi小的数改为HiHi,求NN次操作后数列中所有元素的和。
Solution
看到这道题第一眼就是线段树,但是发现这个在线区间修改真的是很难维护,如果当前的比区间所有的数都大可以直接打标记,但是如果区间有的数比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() {}
本文介绍了USACO竞赛中的一道题目——City Horizon,涉及城市建筑的轮廓面积计算。问题转化为对数列的区间修改操作,通过离线处理和动态开点的方法解决数据范围大的问题。
1425

被折叠的 条评论
为什么被折叠?



