Solution-「ARC 063D」「AT 2149」Snuke's Coloring 2
阿新 • • 發佈:2020-10-25
\(\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; }