[BZOJ3845] ZCC loves Minecraft
2016-02-26 21:23
288 查看
不得不说出题人的创意特赞!
题面翻译(BZOJ的英文题能不能带翻译QaQ我一开始没看见多组数据):
在一个平面直角坐标系中,有一些关键点,坐标都是整数。现在要求你构造一个点集:
1.包含了所有关键点
2.允许由无数个点叠成一条线段,或是平面
3.任意一条平行于x轴或y轴的直线,与这个点集的公共部分,必须是:空,一个点,或连续的一条线段(也就是中间不能断)
4.满足1,3的条件下,使点集中的平面的面积最小
现在一开始平面上没有关键点,N次操作,每次加入一个关键点,输出N行,每行代表此时的点集的最小面积
题目保证,在任意时刻,整个点集一定是联通的,也就是保证不存在一个不联通的点集满足1和3
设所有x的最小值是lmost,最大值是rmost。根据3性质,对于一条直线X=x0,它与点集的公共部分是线段(x0,DOWNx0)−−−(x0,UPx0)(lmost<=x<=rmost)(x_0,DOWN_{x_0})---(x0,UP_{x_0}) (lmost<=x<=rmost)
以UP[lmost..rmost]为例,因为3性质,不存在一个i使得
UPi−1>UPi<UPi+1(lmost<i<rmost)UP_{i-1} > UP_i < UP_{i+1} (lmost
也就是说,UP一定是一个先不下降,后不上升的序列。同理,DOWN先不上升后不下降
当你新加入一个关键点导致UP改变时,有如下两种情况//设原先的y最大值是T
图1是y<=T 图2是Y>T 的情况。
于是我们可以考虑对于所有整数lmost<=X<=rmost,用线段树维护一个UP(DOWN)序列,支持以下操作:
1.区间(单点)变成同一个数
2.询问一个位置X,求X左侧/右侧最近的且>=UP[X]的UP[ANS]
然而怎么得到输出的答案?
先令所有DOWN=0,UP都>0
ANS=∑i=lmostrmost−1min(UP[i],UP[i+1])ANS=\sum_{i=lmost}^{rmost-1}{min(UP[i],UP[i+1])}
上式的正确性是显而易见的,又因为UP是先不降后不升序列,设UP[mid]是最大值,那么:
min(UP[i],UP[i+1])=UP[i](i<mid)min(UP[i],UP[i+1])=UP[i](i
min(UP[i],UP[i+1])=UP[i+1](i>=mid)min(UP[i],UP[i+1])=UP[i+1](i>=mid)
结合上式,ANS=(∑i=lmostrmostUP[i])−maxi=rmosti=lmostUP[i]ANS=(\sum_{i=lmost}^{rmost}UP[i])-max_{i=lmost}^{i=rmost}{UP[i]}
那么如果有DOWN存在,结论也是很容易的:
ANS=(∑i=lmostrmostUP[i])−(∑i=lmostrmostDOWN[i])−maxrmosti=lmostUP[i]+minrmosti=lmostDOWN[i]ANS=(\sum_{i=lmost}^{rmost}UP[i])-(\sum_{i=lmost}^{rmost}DOWN[i])-max_{i=lmost}^{rmost}UP[i]+min_{i=lmost}^{rmost}DOWN[i]
你可以把DOWN记成相反数,那么只要让你的线段树兹磁区间和询问和区间最大值询问就可以啦!
代码长度不忍直视>_<
题面翻译(BZOJ的英文题能不能带翻译QaQ我一开始没看见多组数据):
在一个平面直角坐标系中,有一些关键点,坐标都是整数。现在要求你构造一个点集:
1.包含了所有关键点
2.允许由无数个点叠成一条线段,或是平面
3.任意一条平行于x轴或y轴的直线,与这个点集的公共部分,必须是:空,一个点,或连续的一条线段(也就是中间不能断)
4.满足1,3的条件下,使点集中的平面的面积最小
现在一开始平面上没有关键点,N次操作,每次加入一个关键点,输出N行,每行代表此时的点集的最小面积
题目保证,在任意时刻,整个点集一定是联通的,也就是保证不存在一个不联通的点集满足1和3
设所有x的最小值是lmost,最大值是rmost。根据3性质,对于一条直线X=x0,它与点集的公共部分是线段(x0,DOWNx0)−−−(x0,UPx0)(lmost<=x<=rmost)(x_0,DOWN_{x_0})---(x0,UP_{x_0}) (lmost<=x<=rmost)
以UP[lmost..rmost]为例,因为3性质,不存在一个i使得
UPi−1>UPi<UPi+1(lmost<i<rmost)UP_{i-1} > UP_i < UP_{i+1} (lmost
也就是说,UP一定是一个先不下降,后不上升的序列。同理,DOWN先不上升后不下降
当你新加入一个关键点导致UP改变时,有如下两种情况//设原先的y最大值是T
图1是y<=T 图2是Y>T 的情况。
于是我们可以考虑对于所有整数lmost<=X<=rmost,用线段树维护一个UP(DOWN)序列,支持以下操作:
1.区间(单点)变成同一个数
2.询问一个位置X,求X左侧/右侧最近的且>=UP[X]的UP[ANS]
然而怎么得到输出的答案?
先令所有DOWN=0,UP都>0
ANS=∑i=lmostrmost−1min(UP[i],UP[i+1])ANS=\sum_{i=lmost}^{rmost-1}{min(UP[i],UP[i+1])}
上式的正确性是显而易见的,又因为UP是先不降后不升序列,设UP[mid]是最大值,那么:
min(UP[i],UP[i+1])=UP[i](i<mid)min(UP[i],UP[i+1])=UP[i](i
min(UP[i],UP[i+1])=UP[i+1](i>=mid)min(UP[i],UP[i+1])=UP[i+1](i>=mid)
结合上式,ANS=(∑i=lmostrmostUP[i])−maxi=rmosti=lmostUP[i]ANS=(\sum_{i=lmost}^{rmost}UP[i])-max_{i=lmost}^{i=rmost}{UP[i]}
那么如果有DOWN存在,结论也是很容易的:
ANS=(∑i=lmostrmostUP[i])−(∑i=lmostrmostDOWN[i])−maxrmosti=lmostUP[i]+minrmosti=lmostDOWN[i]ANS=(\sum_{i=lmost}^{rmost}UP[i])-(\sum_{i=lmost}^{rmost}DOWN[i])-max_{i=lmost}^{rmost}UP[i]+min_{i=lmost}^{rmost}DOWN[i]
你可以把DOWN记成相反数,那么只要让你的线段树兹磁区间和询问和区间最大值询问就可以啦!
代码长度不忍直视>_<
[code]#include <cstdio> template <typename Int> inline Int max(Int x, Int y) { return x > y ? x : y; } template <typename Int> inline Int min(Int x, Int y) { return x < y ? x : y; } int I() { char c = getchar(); int r = 0, f = 0; while ((c < 48 or c > 57) && c != '-') c = getchar(); if (c == '-') f = 1, c = getchar(); while (c > 47 && c < 58) r = (r << 3) + r + r + c - 48, c = getchar(); return f ? -r : r; } class segmentTree { public: void PUT(int x, int y) { if (QMax(x, x) < y) { if (y <= SegMax[1]) { int LMt = x > -100000 ? (Ql = -100000, Qr = x - 1, Qw = y, RMostLarger(1, -100000, 100000)) : -1234321237, RMt = x < 100000 ? (Ql = x + 1, Qr = 100000, Qw = y, LMostLarger(1, -100000, 100000)) : 1234321237; if (LMt != -1234321237) Same(LMt + 1, x, y); else Same(x, RMt - 1, y); } else { int TOPp = MaxPosition(), X = leaf_ref[TOPp], Y = SegMax[TOPp]; if (X < x) Same(X, x - 1, Y); if (X > x) Same(x + 1, X, Y); Set(x, y); } } } void init() { __init(1, -100000, 100000); } void Set(int x, int d) { M(x, d); } void Same(int l, int r, int d) { Ql = l, Qr = r, Qw = d, P(1, -100000, 100000); } int QMax(int l, int r) { Ql = l, Qr = r; return G(1, -100000, 100000); } long long QSum(int l, int r) { Ql = l, Qr = r; return S(1, -100000, 100000); } int MaxPosition() { int p = 1; while (Size[p] > 1) { Down(p); if (SegMax[p] == SegMax[p + p]) p <<= 1; else p = p + p + 1; } return p; } private: int RMostLarger(int p, int l, int r) { if (SegMax[p] < Qw) return -1234321237; if (Ql <= l && r <= Qr) { int m; while (Size[p] > 1) { m = l + r >> 1; Down(p); if (SegMax[p + p + 1] >= Qw) l = m + 1, p = p + p + 1; else r = m, p <<= 1; } return leaf_ref[p]; } int __, m = l + r >> 1; if (Qr > m) if ((__ = RMostLarger(p + p + 1, m + 1, r)) != -1234321237) return __; return Ql <= m ? RMostLarger(p + p, l, m) : -1234321237; } int LMostLarger(int p, int l, int r) { if (SegMax[p] < Qw) return 1234321237; if (Ql <= l && r <= Qr) { int m; while (Size[p] > 1) { m = l + r >> 1; Down(p); if (SegMax[p + p] >= Qw) r = m, p <<= 1; else l = m + 1, p = p + p + 1; } return leaf_ref[p]; } int __, m = l + r >> 1; if (Ql <= m) if ((__ = LMostLarger(p + p, l, m)) != 1234321237) return __; return Qr > m ? LMostLarger(p + p + 1, m + 1, r) : 1234321237; } int Ql, Qr, Qw; int SegMax[555555], SameTag[555555], Size[555555], leaf_ref[555555]; long long SegSum[555555]; void __init(int p, int l, int r) { Size[p] = r - l + 1; if (l < r) { int m = l + r >> 1; __init(p + p, l, m); __init(p + p + 1, m + 1, r); } else leaf_ref[p] = l; } void Up(int p) { SegMax[p] = max(SegMax[p + p], SegMax[p + p + 1]); SegSum[p] = SegSum[p + p] + SegSum[p + p + 1]; } void Down(int p) { if (SameTag[p]) { SameTag[p] = 0; SameTag[p + p] = SameTag[p + p + 1] = 1; SegMax[p + p] = SegMax[p + p + 1] = SegMax[p]; SegSum[p + p] = (long long)Size[p + p] * SegMax[p]; SegSum[p + p + 1] = (long long)Size[p + p + 1] * SegMax[p]; } } int Find(int x) { int p = 1, l = -100000, r = 100000, m; Down(p); while (l < r) { Down(p); m = l + r >> 1; if (x <= m) r = m, p <<= 1; else l = m + 1, p = p + p + 1; } return p; } void P(int p, int l, int r) // makeSame { if (Ql <= l && r <= Qr) SameTag[p] = 1, SegMax[p] = Qw, SegSum[p] = (long long)Size[p] * Qw; else { int m = l + r >> 1; Down(p); if (Ql <= m) P(p + p, l, m); if (Qr > m) P(p + p + 1, m + 1, r); Up(p); } } int G(int p, int l, int r) // get the largest { if (Ql <= l && r <= Qr) return SegMax[p]; Down(p); int m = l + r >> 1; int Le = Ql <= m ? G(p + p, l, m) : 0, Ri = Qr > m ? G(p + p + 1, m + 1, r) : 0; return max(Le, Ri); } void M(int x, int d) // a[x] := d { int p = Find(x); SegMax[p] = SegSum[p] = d, SameTag[p] = 0; while (p > 1) p >>= 1, Up(p); } long long S(int p, int l, int r) // get the sum { if (Ql <= l && r <= Qr) return SegSum[p]; Down(p); int m = l + r >> 1; long long Le = Ql <= m ? S(p + p, l, m) : 0, Ri = Qr > m ? S(p + p + 1, m + 1, r) : 0; return Le + Ri; } }UP, DOWN; int main() { UP.init(), DOWN.init(); int Q; while (scanf("%d", &Q) != EOF) { int lmost = 10000000, rmost = -10000000; UP.Same(-100000, 100000, 0), DOWN.Same(-100000, 100000, 0); while (Q--) { int x = I(), y = I(); UP.PUT(x, 200000 + y), DOWN.PUT(x, 200000 - y); if (x < lmost) lmost = x; if (x > rmost) rmost = x; printf("%lld\n", UP.QSum(-100000, 100000) + DOWN.QSum(-100000, 100000) - UP.QMax(-100000, 100000) - DOWN.QMax(-100000, 100000) - 400000ll * (rmost - lmost)); } } return 0; }
相关文章推荐
- OpenGL入门学习(十三)
- 记录重要博客链接
- 1065. A+B and C (64bit) (20)
- 大型网站架构演变和知识体系
- BZOJ3998: [TJOI2015]弦论
- vim 解决中文乱码,设置高亮,共享粘贴板
- 160227、javascript特效
- 关于子网掩码(Netmask)
- iOS-OC-基本控件之UIPageControl
- UIDatePicker
- processing 鼠标光标显隐设置
- 求n的阶乘后导0的个数
- PCA 和 SVD
- Web前端环境搭建篇之-- 利用NodeJS来安装Ionic和Cordova
- 0819-TableView(tableVeiw控件的代理)(tableView单组 lol数据展示)(tableView-汽车品牌logo 右侧a-z)(KVC)(tableView - 添加删除按钮出现)(自定义代理delegate)(内存)
- php laravel mysql无法连接处理方案(linux服务器配置)
- hdu1160 FatMouse's Speed(LIS普通法)
- PHP常见面试题(简答部分一)
- mysql root密码丢失
- 一些关于树莓派的教程整理