Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 403|回复: 0

[默认分类] AtCoder Regular Contest 063 F : Snuke’s Coloring 2 (线段树 + 单调栈)

  [复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-4-3 11:27:52 | 显示全部楼层 |阅读模式

    题意 :

    小 \(\mathrm{C}\) 很喜欢二维染色问题,这天他拿来了一个 \(w × h\) 的二维平面 , 初始时均为白色 . 然后他在上面设置了 \(n\) 个关键点 \((X_i , Y_i)\) , 对于每个关键点他会选择进行下列操作的一个 :

      将 \(x > X_i\) 的部分染成黑色.
      将 \(x < X_i\) 的部分染成黑色.
      将 \(y > Y_i\) 的部分染成黑色.
      将 \(y < Y_i\) 的部分染成黑色.
      
    现在让你 , 最大化所有操作结束之后白色部分的 周长 如果白色部分没有输出 \(0\)
    \((0 \le n ≤ 2 \times 10^5 , 1 \le w,h \le 10^8, 0\le X_i \le w, 0 \le Y_i \le h)\)


    题解 :

    我先摘一段 dy0607 的题解qwq (Orz dyy)

    题目实际上是要找一个周长最大的矩形 , 内部不包含任何关键点.
    可以发现一个小性质 : 答案的下界为 \(2 × (max(w, h) + 1)\) ,
    因此这个矩形一定会经过 \(x = \frac{w}{2}\) 或 \(y = \frac{h} {2}\) . 先考虑经过 \(x = \frac{w}{2}\) 的情况 , 另一种情况是一样的.
    先将坐标离散化.枚举矩形的上边界 \(y_R\) ,对于每一个下边界 \(y_L\) , 我们可以计算出矩形的最优左边界 \(x_L = min \{X_i |Y_i ∈ [y_L , y_R ], X_i > \frac{w}{2} \}\) , 以 及 右 边 界 \(x_R = max \{X_i |Y_i ∈[y_L , y_R ], X_i ≤ \frac{w} {2} \}\) ,
    此时可以找到一个周长为 \(2 × (x R − x L + y R − y L )\) 的矩形.
    直接做是 \(O(n^2)\) 的,但该算法可以用线段树优化,在将上边界往上移的过程中动态维护每
    个位置的 \(x_L , x_R\) ,并维护全局最小值,不难发现只需要左右各开一个单调栈,在更新单调栈
    时在线段树树上进行区间加减即可. \(O(n \log n)\) .
    就算没有观察到上面的小性质,也可以多套一层分治解决,复杂度多一个 \(\log\) .

    说的很轻松 其实很难理解
    \(\Theta(n^2)\) 的算法 就是运用了那个小性质 每次枚举上边界 滑动的时候 一遍更新 一遍算下答案 就行了
    我们主要是考虑 \(\Theta(n \log n)\) 的算法
    如何理解呢
    其实我们就是枚举了一个上边界 , 然后对于这个上边界时 所有下边界的最优解都存在线段树中
    (存的是当前 当前的顶到这里的宽 -下底界的高度 )
    主要讲一下单调栈是干什么的
    其中元素是一个个从栈底到栈顶是逐渐远离中线的线段
    其中有两个维度 一个是维护这条线段的上端
    另一个是维护这条线段的这条线段的横坐标 用来算和中线的长度
    然后维护了这个有什么用呢 0.0
    就是你考虑如下一种情况

    (虚线为中线 黑色是当前单调栈里的 红色是现在将过来的一个线段)
    我们现在要过来的线段 将会更新答案
    所以我们将两个栈顶线段的答案进行更改 将这些线段的横着的答案变小它坐标的相应的差值
    这个就可以直接在线段树上做加减法就行了
    然后我们用这条绿色和红色的线段一起 共同构成一个新的线段存进单调栈中去
    记得前面线段树存的什么嘛
    就是一个点当前宽与底坐标的差值 然后顶 (就是后一个线段的纵坐标) 又是固定的 那么我们用顶减去底 然后加上当前宽
    就得到了当前矩形一半周长的最优答案
    然后为了解决两个相邻直接当上下界的答案 我们每次结束要在单调栈中多加一个元素 (横坐标为边界) 就行了
    然后坐标翻转再做一遍 就行了就是可能跨了另一条中线qwq
    代码比较巧妙 一定要对着理解!!

    1. [code]#include <bits/stdc++.h>
    2. #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    3. #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    4. #define Set(a, v) memset(a, v, sizeof(a))
    5. using namespace std;
    6. inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    7. inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    8. inline int read() {
    9.     int x = 0, fh = 1; char ch = getchar();
    10.     for (; !isdigit(ch); ch = getchar()) if (ch == "-") fh = -1;
    11.     for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    12.     return x * fh;
    13. }
    14. void File() {
    15.     freopen ("paint.in", "r", stdin);
    16.     freopen ("paint.out", "w", stdout);
    17. }
    18. const int N = 200010;
    19. int w, h, n;
    20. struct Segment_Tree {
    21.     int maxv[N << 2], add[N << 2];
    22.     void Init() { Set(maxv, 0); Set(add, 0); }
    23.     void Update(int o, int l, int r, int ul, int ur, int uv) {
    24.         if (ul <= l && r <= ur) { maxv[o] += uv; add[o] += uv; return ; }
    25.         int mid = (l + r) >> 1;
    26.         if (ul <= mid) Update(o << 1, l, mid, ul, ur, uv);
    27.         if (ur > mid) Update(o << 1 | 1, mid + 1, r, ul, ur, uv);
    28.         maxv[o] = max(maxv[o << 1], maxv[o << 1 | 1]) + add[o];
    29.     }
    30. } T;
    31. typedef pair<int, int> PII;
    32. #define x first
    33. #define y second
    34. #define mp make_pair
    35. PII lt[N];
    36. PII sta[N], stb[N];
    37. int topa, topb;
    38. int ans = 0;
    39. void Work() {
    40.     sort(lt + 1, lt + 1 + n);
    41.     T.Init();
    42.     topa = topb = 0;
    43.     For (i, 1, n - 1) {
    44.         if (lt[i].y <= h / 2) {
    45.             int Next = i - 1;
    46.             while (topa && lt[i].y > sta[topa].y) {
    47.                 T.Update(1, 1, n, sta[topa].x, Next, sta[topa].y - lt[i].y);
    48.                 Next = sta[topa].x - 1; -- topa;
    49.             }
    50.             if (Next != i - 1) sta[++ topa] = mp(Next + 1, lt[i].y);
    51.         } else {
    52.             int Next = i - 1;
    53.             while (topb && lt[i].y < stb[topb].y) {
    54.                 T.Update(1, 1, n, stb[topb].x, Next, lt[i].y - stb[topb].y);
    55.                 Next = stb[topb].x - 1; -- topb;
    56.             }
    57.             if (Next != i - 1) stb[++ topb] = mp(Next + 1, lt[i].y);
    58.         }
    59.         sta[++ topa] = mp(i, 0);
    60.         stb[++ topb] = mp(i, h);
    61.         T.Update(1, 1, n, i, i, h - lt[i].x);
    62.         chkmax(ans, T.maxv[1] + lt[i + 1].x);
    63.     }
    64. }
    65. int main () {
    66.     File();
    67.     w = read(); h = read(); n = read();
    68.     For (i, 1, n) {
    69.         lt[i].x = read();
    70.         lt[i].y = read();
    71.     }
    72.     lt[++ n] = mp(0, 0);
    73.     lt[++ n] = mp(w, h);
    74.     Work();
    75.     For (i, 1, n)
    76.         swap(lt[i].x, lt[i].y);
    77.     swap(w, h);
    78.     Work();
    79.     printf ("%d\n", ans << 1);
    80.     return 0;
    81. }
    复制代码
    [/code]
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-17 01:22 , Processed in 0.419436 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表