\(\mathcal{Decription}\)

  Link.

  平面上有一个左下角坐标 \((0,0)\) 右上角坐标 \((W,H)\) 的矩形,起初长方形内部被涂白。

现在给定 \(n\) 个点,你每次在以下 \(4\) 种操作中选择一种:

  • 将矩形内 \(x<x_i\) 的区域涂黑;
  • 将矩形内 \(x>x_i\) 的区域涂黑;
  • 将矩形内 \(y<y_i\) 的区域涂黑;
  • 将矩形内 \(y>y_i\) 的区域涂黑。

  最大化操作后白色矩阵周长。

  \(n\le3\times10^5\),\(W,H\le10^8\)。

\(\mathcal{Solution}\)

  就挺 amazing 的题呐。

  题意等价于求周长最大的矩形,使得矩形内不包含任意一个点。

  首先,答案有下界 \(2\max\{H,W\}+2\)。考虑一个周长超过该下界的矩形,它一定跨过 \(y=\frac{H}2\) 或 \(x=\frac{W}2\),所以只需要分别求出跨过着两条直线的周长最大的合法矩形。下以跨过 \(l:y=\frac{H}2\) 的情形为例。

  从左到右用一条扫描线,设当前 \(x=x_0\) 轴上高于 \(l\) 的最低点离 \(l\) 的距离为 \(u\),低于 \(l\) 的最高点离 \(l\) 的距离为 \(d\),那么当矩形过 \(x=x_0\) 时,高度 \(\le u+d\)。

  接下来,把从 \(x_1\) 到 \(x_2\) 的横向宽度表达为 \((W-x_1)-(W-x_2)\),然后线段树维护矩形左边界为 \(x=x_0\) 时,高度 \(+(W-x_1)\) 的值。用单调递减的单调栈维护 \(u\) 和 \(d\),弹栈时修改一段区间的值,最后求前缀最大值即可。(建议参照代码理解。)

\(\mathcal{Code}\)

/* Clearink */

#include <stack>
#include <cstdio>
#include <algorithm> inline int rint () {
int x = 0; char s = getchar ();
for ( ; s < '0' || '9' < s; s = getchar () );
for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
return x;
} inline int min_ ( const int a, const int b ) { return a < b ? a : b; }
inline int max_ ( const int a, const int b ) { return a < b ? b : a; } const int MAXN = 3e5;
int n, W, H, x[MAXN + 5], up[MAXN + 5], dn[MAXN + 5];
std::stack<int> stku, stkd; struct Point {
int x, y;
inline bool operator < ( const Point& p ) const { return x < p.x; }
} p[MAXN + 5]; struct SegmentTree {
int mx[MAXN << 2], tag[MAXN << 2]; inline void pushup ( const int rt ) {
mx[rt] = max_ ( mx[rt << 1], mx[rt << 1 | 1] );
} inline void pushdn ( const int rt ) {
int& k = tag[rt];
if ( !k ) return ;
mx[rt << 1] += k, tag[rt << 1] += k;
mx[rt << 1 | 1] += k, tag[rt << 1 | 1] += k;
k = 0;
} inline void clear ( const int rt, const int l, const int r ) {
mx[rt] = tag[rt] = 0;
if ( l == r ) return ;
int mid = l + r >> 1;
clear ( rt << 1, l, mid ), clear ( rt << 1 | 1, mid + 1, r );
} inline void update ( const int rt, const int l, const int r,
const int ul, const int ur, const int uv ) {
if ( ul <= l && r <= ur ) return mx[rt] += uv, tag[rt] += uv, void ();
int mid = l + r >> 1; pushdn ( rt );
if ( ul <= mid ) update ( rt << 1, l, mid, ul, ur, uv );
if ( mid < ur ) update ( rt << 1 | 1, mid + 1, r, ul, ur, uv );
pushup ( rt );
} inline int query ( const int rt, const int l, const int r,
const int ql, const int qr ) {
if ( ql <= l && r <= qr ) return mx[rt];
int mid = l + r >> 1, ret = 0; pushdn ( rt );
if ( ql <= mid ) ret = query ( rt << 1, l, mid, ql, qr );
if ( mid < qr ) ret = max_ ( ret, query ( rt << 1 | 1, mid + 1, r, ql, qr ) );
return ret;
}
} segt; inline int solve ( const int n, const int W, const int H ) {
if ( !n ) return 0;
int mid = H >> 1, cnt = 0, ret = 0;
for ( ; !stku.empty (); stku.pop () );
for ( ; !stkd.empty (); stkd.pop () );
segt.clear ( 1, 1, n );
stku.push ( 0 ), stkd.push ( 0 );
for ( int i = 1; i <= n; ) {
x[++ cnt] = p[i].x;
up[cnt] = H - mid, dn[cnt] = mid;
for ( ; i <= n && p[i].x == x[cnt]; ++ i ) {
if ( p[i].y >= mid ) up[cnt] = min_ ( up[cnt], p[i].y - mid );
if ( p[i].y <= mid ) dn[cnt] = min_ ( dn[cnt], mid - p[i].y );
}
int las;
while ( up[las = stku.top ()] > up[cnt] ) {
stku.pop ();
segt.update ( 1, 1, n, stku.top () + 1, las, up[cnt] - up[las] );
}
stku.push ( cnt );
while ( dn[las = stkd.top ()] > dn[cnt] ) {
stkd.pop ();
segt.update ( 1, 1, n, stkd.top () + 1, las, dn[cnt] - dn[las] );
}
stkd.push ( cnt );
segt.update ( 1, 1, n, cnt, cnt, W - x[cnt - 1] + up[cnt] + dn[cnt] );
ret = max_ ( ret, segt.query ( 1, 1, n, 1, cnt ) - W + p[i].x );
}
return ret;
} int main () {
W = rint (), H = rint (), n = rint ();
for ( int i = 1; i <= n; ++ i ) {
p[i].x = rint (), p[i].y = rint ();
if ( !p[i].x || p[i].x == W || !p[i].y || p[i].y == H ) -- i, -- n;
}
int ans = 0;
std::sort ( p + 1, p + n + 1 ), p[n + 1].x = W;
for ( int i = 1; i <= n + 1; ++ i ) {
ans = max_ ( ans, H + p[i].x - p[i - 1].x );
}
ans = max_ ( ans, solve ( n, W, H ) );
for ( int i = 1; i <= n; ++ i ) p[i].x ^= p[i].y ^= p[i].x ^= p[i].y;
std::sort ( p + 1, p + n + 1 ), p[n + 1].x = H;
for ( int i = 1; i <= n + 1; ++ i ) {
ans = max_ ( ans, W + p[i].x - p[i - 1].x );
}
ans = max_ ( ans, solve ( n, H, W ) );
printf ( "%d\n", ans << 1 );
return 0;
}

Solution -「ARC 063D」「AT 2149」Snuke's Coloring 2的更多相关文章

  1. Solution Set -「ARC 107」

    「ARC 107A」Simple Math   Link.   答案为: \[\frac{a(a+1)\cdot b(b+1)\cdot c(c+1)}{8} \] 「ARC 107B」Quadrup ...

  2. 「ARC 139F」Many Xor Optimization Problems【线性做法,踩标】

    「ARC 139F」Many Xor Optimization Problems 对于一个长为 \(n\) 的序列 \(a\),我们记 \(f(a)\) 表示从 \(a\) 中选取若干数,可以得到的最 ...

  3. Solution -「CTS 2019」「洛谷 P5404」氪金手游

    \(\mathcal{Description}\)   Link.   有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...

  4. 【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇

    http://www.4gamer.net/games/216/G021678/20140714079/     连载第2回的本回,  Arc System Works开发的格斗游戏「GUILTY G ...

  5. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  6. Android内存管理(4)*官方教程 含「高效内存的16条策略」 Managing Your App's Memory

    Managing Your App's Memory In this document How Android Manages Memory Sharing Memory Allocating and ...

  7. SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法

    用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助 ...

  8. 「Windows MFC 」「Edit Control」 控件

    「Windows MFC 」「Edit Control」 控件

  9. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

随机推荐

  1. 微信小程序配置域名的时候提示“校验文件验证失败”

    在微信小程序后台配置web-view的业务域名跟扫普通链接二维码打开小程序两项功能时, 一直提示"校验文件验证失败,请下载校验文件,上传到服务器指定的目录" 实际访问校验文件的路径 ...

  2. MRCTF2020 你传你🐎呢

    MRCTF2020 你传你 .htaccess mime检测 1.先尝试上传了一个文件,发现.jpg后缀的可以上传成功,但是用蚁剑连接时返回空数据 2.重新先上传一个.htaccess文件,让需要被上 ...

  3. [STM32F10x] 利用定时器测量频率

    硬件:STM32F103C8T6 平台:ARM-MDk V5.11 原理 利用STM32F10x的定时器的捕获(Capture)单元测量输入信号的频率. 基本原理是通过两次捕获达到的计数器的差值,来计 ...

  4. c#操作符详解

    操作符概览 操作符(Operator)也译为"运算符" 操作符是用来操作数据的,被操作符操作的数据称为操作数(Operand) 操作符的本质 操作符的本质是函数(即算法)的&quo ...

  5. 多线程创建的方式一(继承Thread类)

    1 package multithread; 2 3 /* 4 * 如何创建一个线程呢? 5 * 6 * 创建线程方式一:继承Thread类. 7 * 8 * 步骤: 9 * 1,定义一个类继承Thr ...

  6. linux编译安装(全面教程解析)

    目录 一:编译安装 1.编译安装特点 2.编译安装 简介 编译安装 1.使用源代码,编译打包软件 2,编译安装,只能按照源代码 一:编译安装 1.编译安装特点 1.可以自定制软件 2.按需求构建软件 ...

  7. 我在 Gitee 上发现了一个简洁又好用的网络音乐播放器!

    这几天无聊的时候我想听听歌,但我想要找一个简单快速的网络音乐播放器来用用.这时我在 Gitee 上看见一个看上去不错的开源项目 -- Hi音乐. 项目链接:https://gitee.com/hi-j ...

  8. iBooker AI+财务提升星球 2020.4 热门讨论

    比特币量化套利的心路历程(附python量化招聘)(分享自知- 如何选择一份好的工作? 你知道为什么大家都想去好公司吗? 不- #财务知识# 可转债套利 辉丰转债128012套利之三个知道- #财务知 ...

  9. CKKS加密方案

    本文内容来自"Protecting Privacy throughHomomorphic Encryption",主要学习里面的CKKS部分. CKKS是一种同态加密方案,其安全性 ...

  10. 基础学习:关于this在派生类构造函数中的理解

    https://www.cnblogs.com/Bear-Study-Hard/archive/2006/01/09/313551.html 看了上面这篇文章有感,特做了个小样板,以加深对于this在 ...