[arc063]F.すぬけ君の塗り絵2
因为这题考虑可以观察一个性质,答案的下界为 \(2×(max(w,h)+1)\), 因为你至少可以空出一行或一列,因此这个矩形一定会经过 \(x=\frac{w}{2}\) 或 \(y=\frac{h}{2}\) . 先考虑经过 \(\frac{w}{2}\) 的情况 , 另一种情况是一样的.
先将坐标离散化.枚举矩形的上边界 \(yR\) ,对于每一个下边界 \(yL\) , 我们可以计算出矩形的最优左边界 \(xL=min\{Xi|Yi\in[yL,yR],Xi>\frac{w}{2}\}\) , 以 及 右 边 界 \(xR=max\{Xi|Yi\in[yL,yR],Xi≤\frac{w}{2}\}\) ,
此时可以找到一个周长为 \(2×(xR−xL+yR−yL)\) 的矩形.
直接做是 \(O(n^2)\) 的,但该算法可以用线段树优化,在将上边界往上移的过程中动态维护每
个位置的 xL,xR,并维护全局最小值,不难发现只需要左右各开一个单调栈,在更新单调栈
时在线段树树上进行区间加减即可. O(nlogn) .
这其实就是一个计算极大subrectangle的过程, 因为知道中间边界, 基本原理来自于暴力, 就是简单的枚举长度, 更新宽度. 其实这题也很套路, 发现这题的边界是不断移动的, 在移动的时候会产生一部分的重复信息, 所以考虑采用数据结构维护, 分析一下颓余状态, 发现可以单调栈优化.
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = 3e5 + 9;
struct Point {
int x, y;
int operator < (const Point &a) const {
return x < a.x;
}
}s[Maxn];
int n, ans, h, w;
void init() {
w = read(), h = read(); n = read();
rep (i, 1, n) s[i].x = read(), s[i].y = read();
s[++n] = (Point){0, 0};
s[++n] = (Point){w, h};
}
pair<int, int> stkA[Maxn], stkB[Maxn];
int topa, topb;
struct SGMTtree {
int tree[Maxn << 2], add[Maxn << 2];
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
void clear() {
clar(tree, 0), clar(add, 0);
}
void pushup(int rt) {
tree[rt] = max(tree[lc(rt)], tree[rc(rt)]);
}
void pushdown(int rt) {
if (add[rt]) {
add[lc(rt)] += add[rt]; add[rc(rt)] += add[rt];
tree[lc(rt)] += add[rt]; tree[rc(rt)] += add[rt];
add[rt] = 0;
}
}
void modify(int rt, int l, int r, int p, int q, int v) {
if (p <= l && r <= q) {
add[rt] += v; tree[rt] += v;
return ;
}
int mid = (l + r) >> 1; pushdown(rt);
if (q <= mid) modify(ls, p, q, v);
else if (p >= mid + 1) modify(rs, p, q, v);
else modify(ls, p, q, v), modify(rs, p, q, v);
pushup(rt);
}
#undef lc
#undef rc
#undef ls
#undef rs
}st;
void ZhaoQingFei() {
sort(s + 1, s + n + 1);
st.clear(); topa = topb = 0;
rep (i, 1, n) {
int Nxt = i - 1;
if (s[i].y <= h / 2) {
while (topa && stkA[topa].second <= s[i].y) {
st.modify(1, 1, n, stkA[topa].first, Nxt, stkA[topa].second - s[i].y);
Nxt = stkA[topa].first - 1; --topa;
}
if (Nxt != i - 1) stkA[++topa] = make_pair(Nxt + 1, s[i].y);
} else {
while (topb && stkB[topb].second >= s[i].y) {
st.modify(1, 1, n, stkB[topb].first, Nxt, s[i].y - stkB[topb].second);
Nxt = stkB[topb].first - 1; --topb;
}
if (Nxt != i - 1) stkB[++topb] = make_pair(Nxt + 1, s[i].y);
}
stkA[++topa] = make_pair(i, 0);
stkB[++topb] = make_pair(i, h);
st.modify(1, 1, n, i, i, h - s[i].x);
ans = max(ans, s[i + 1].x + st.tree[1]);
}
}
void solve() {
ZhaoQingFei();
rep (i, 1, n) swap(s[i].x, s[i].y);
swap(h, w);
ZhaoQingFei();
cout << ans * 2 << endl;
}
int main() {
// freopen("ARC047B.in", "r", stdin);
// freopen("ARC047B.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}
更新防死
[arc063]F.すぬけ君の塗り絵2的更多相关文章
- ARC063F すぬけ君の塗り絵 2 / Snuke's Coloring 2
题面 一句话题面:给你一些点,求这些点之中夹的最大的矩形周长.(考虑边界) Solution 首先是一个结论,答案矩形一定经过\(x=\frac{w}{2}\)或经过\(y=\frac{h}{2}\) ...
- すぬけ君の塗り絵 / Snuke's Coloring AtCoder - 2068 (思维,排序,贡献)
Problem Statement We have a grid with H rows and W columns. At first, all cells were painted white. ...
- 【AtCoder】ARC063
ARC063 C - 一次元リバーシ / 1D Reversi 不同的颜色段数-1 #include <bits/stdc++.h> #define fi first #define se ...
- 【AtCoder】ARC061
ARC061 C - たくさんの数式 / Many Formulas 这个其实\(10^5\)也能做.. 就是\(dp[i]\)表示到第i位的方案数,\(sum[i]\)表示延伸到第i位之前的所有方案 ...
- DP题组
按照顺序来. Median Sum 大意: 给你一个集合,求其所有非空子集的权值的中位数. 某集合的权值即为其元素之和. 1 <= n <= 2000 解: 集合配对,每个集合都配对它的补 ...
- csp退役前的做题计划1(真)
csp退役前的做题计划1(真) 因为我太菜了,所以在第一次月考就会退役,还是记录一下每天做了什么题目吧. 任务计划 [ ] Z算法(Z Algorithm) 9.28 [x] ARC061C たくさん ...
- es6 解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 关于给变量赋值,传统的变量赋值是这样的: var arr = [1,2,3];//把数组的值 ...
- php debug二三事
最近php相关项目遇到了一些问题,处理起来让人感觉挺有意思,寥寥记下. 1.php执行后常驻内存里,需要kill掉该进程再重启,才能让修改后的代码生效. 之前有一次组里小伙伴修改了一个长期后台进程运行 ...
- Unicode字符集的由来
*:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !impor ...
随机推荐
- Oracle 数据库基础 出现invalid username/password; logon denied错误解决方案
出现invalid username/password; logon denied错误解决方案 [win7或win8中 cmd要以管理员身份运行] 1.Dos下输入命令:sqlplus sys/安装时 ...
- hadoop,帮我解了部分惑的文章
http://blog.csdn.net/qianshangding0708/article/details/47423613
- codeforces D. Toy Sum 解题报告
题目链接:http://codeforces.com/problemset/problem/405/D 题目意思:从 1 - 1000000 中选择 n 个数:x1,x2,...,xn,对 x1-1, ...
- hdu-5358 First One(尺取法)
题目链接: First One Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Other ...
- openssl生成公钥私钥对 加解密
在计算机软件开发世界中,编程语言种类极多,数据在各种语言的表现形式可能有所差异,但数据本身的处理可能,或者说本质上是完全一样的:比如数据在某个算法中的运算过程是一样的.在这里,我以加密与解密来作为例子 ...
- AndroidManifest中的Intent-filter标签
经过测试,intent-filter标签中的: 1. <action android:name="android.intent.action.MAIN" /> 代表这是 ...
- bzoj 3481 DZY Loves Math III——反演+rho分解质因数
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3481 推推式子发现:令Q=gcd(P,Q),ans=Σ(d|Q) d*phi(P/d).把 ...
- rsync(三)算法原理和工作流程分析
在开始分析算法原理之前,简单说明下rsync的增量传输功能. 假设待传输文件为A,如果目标路径下没有文件A,则rsync会直接传输文件A,如果目标路径下已存在文件A,则发送端视情况决定是否要传输文件A ...
- 使用Java进行udp-demo编程时碰到的consumer和producter无法连接并报出“java.net.SocketException: Can't assign requested address”问题
在用Java编写了一个udp生产者和消费者的demo时,在生产者启动的时候会抛出异常 java.net.SocketException: Can't assign requested address ...
- 常用模块 re模块与正则表达式
re模块 正则: 正则就是用一些具有特殊含义的符号组合到一起(称之为正则表达式)来描述字符或字符串的方法.或者说:正则就是用描述一类事物的规则.(在python中) 它内嵌在python中,并通过re ...