qoj.#2610 Build a City 补题记录
不是人类可以做出来的题目。link
首先 \(\mathcal O(n^2)\) 的 DP 是简单的。
这里场上犯了一个大失误,我一直在想利用 \(f_i / g_j\) 来收缩 DP 状态,事实上这是根本不可能的。稍微思考一下发现其实每个状态 \((i, j)\) 都是有用的。
既然无法减少状态数,只能考虑整体 dp 来优化了。
因为是二维平面,所以扫描线。考虑从 \(x - 1 \to x\),记 \(p_y\) 表示最大的 \(i\) 满足 \((i, y)\) 可达。
在加入 \(x\) 这一列后,有用的更新应是直接扩展到横坐标为 \(x\) 的状态。考虑我们有三种转移:
-
第一种转移:\(X < x_i\) 且 \(Y < y_i\)
-
第二种转移:\(X < x_i\) 且 \(Y \ge y_i\)
-
第三种转移:\(X \ge x_i\) 且 \(Y < y_i\)
对于第一种转移,我们只需要求一下 \(\max\limits_{y\le y_i - 1} \{ p_y + y \}\),如果可以转移则将 \(p_{y_i}\) 修改为 \(x\)。
对于第二种转移,记 \(y_{\min} = \min\{ y_i | (x_i = x, y_i) \}\),则只有 \(y\ge y_{\min}\) 的部分才能转移。
在 \(y\ge y_{\min}\) 的部分中,我们仅保留 \(2(x - p_y) + y\le m\) 的 \(y\),然后将这些 \(p_y\) 修改为 \(x\)。
对于第三种转移,我们考虑 \(\{ a_1, a_2, \dots, a_k \} = \{y_i | (x_i \le x, y_i)\}\),然后枚举 \(i = 1\dots k - 1\),若 \(p_{a_i} = x\) 且 \(2(a_{i + 1} - a_i) + x\le m\) 则将 \(p_{a_{i + 1}}\) 修改为 \(x\)。
为了方便,对于那些 \(\{y_i | (x_i \le x, y_i)\}\) 中未出现的 \(y\),不妨设 \(p_y = -1\)。
考虑如何维护这三种转移。对于第一种转移,看起来比较容易,似乎不是瓶颈(这可能决定了使用什么数据结构)。
对于第二种转移,感受一下,我们修改的是后缀中那些保留下来的位置。对于那些未保留的位置,我们考虑将它们的 \(p\) 值修改为 \(0\),发现这并不影响。因为这些位置以后也不可能保留下来,或者进行第一种转移。直到从前面转移过来,使得 \(p\) 值再次生效。
这样做的好处就是整个 \(p\) 序列中去掉 \(-1\) 和 \(0\) 后单调不降,有了优化的可能性。
对于第一种转移,我们考虑按照 \(y\) 从小到大的顺序加入新点。
对于第三种转移,我们相当于要做一个有 gap 的前缀 max 更新。在第一种转移中,每加入一个新点后,由于 gap 会变化,所以要重新 upd 一下。
对于 \(p\) 序列,我们考虑其去掉 \(-1\) 和 \(0\) 后“大致”的同色连续段(换言之,用一个单调栈,每次 pop 一段后缀后加入一段以 \(y_{\min}\) 为起点的颜色为 \(x\) 的连续段)。
不难发现,扫描线时每次处理完 \(x - 1\to x\) 后每个同色连续段中都已经满足“每个 gap 连续段的 \(p\) 单调不降”。
也就是说,我们只需要在 pop 出的那些同色连续段之间 upd 一下即可。
【如何 upd】:线段树二分找到右边第一个超出限制的 gap,然后把这一段的 \(p_y\) 覆盖为 \(x\)。
这道题我认为我的赛时犯了很大的错误,不应该死磕一个方向,这样还可能让自己忘记了其他的做法的可能性,模糊了每个可能性的边界。
然后就是我认为在想到整体 dp 之后的两个比较大的难点(个人认为有一定的感受一下猜结论的成分)
-
将未保留的位置覆盖为 \(0\) 后,单调不降。这主要是因为每次覆盖的是一个后缀,应该对每次 cover 一个后缀敏感一些,想到单调栈之类的。
-
每个同色连续段内的“每个 gap 连续段的 \(p\) 单调不降”。这主要是因为将 \(x - 1\to x\) 的所有更新修改都结束后,每个同色连续段确实就有这样的性质,才得以优化。
这需要一定的:
-
直觉,也就是多感受一下。
-
操作的观察,观察每次操作的形式,以及结果。
-
多猜猜结论,然后归纳证明。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define LL long long
#define pb push_back
#define pir pair <ll, ll>
#define mkp make_pair
#define fi first
#define se second
using namespace std;
template <class T>
void rd(T &x) {
char ch; bool f = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') f = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(f) x = -x;
}
const ll maxn = 5e5 + 10, mod = 998244353, inf = 1e9 + 5;
ll power(ll a, ll b = mod - 2, ll p = mod) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a % p;
a = 1ll * a * a % p, b >>= 1;
} return s;
}
template <class T1, class T2>
void add(T1 &x, const T2 y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
void sub(T1 &x, const T2 y) { x = x < y? x + mod - y : x - y; }
template <class T1, class T2>
ll pls(const T1 x, const T2 y) { return x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
ll mus(const T1 x, const T2 y) { return x < y? x + mod - y : x - y; }
template <class T1, class T2>
void chkmin(T1 &a, const T2 b) { a = a < b? a : b; }
template <class T1, class T2>
void chkmax(T1 &a, const T2 b) { a = a < b? b : a; }
ll T, n, x[maxn], y[maxn], hx[maxn], hy[maxn], hxt, hyt;
LL m;
set <ll> st;
vector <ll> vec[maxn];
struct Data { ll l, r, x; } stk[maxn]; ll top;
ll s[maxn], len;
struct SGT {
ll cov1[maxn << 2], cov2[maxn << 2], mxgap[maxn];
ll f0[maxn << 2], f1[maxn << 2], f2[maxn << 2], f3[maxn << 2];
pir mx[maxn << 2];
void addtag(ll p, ll v1, ll v2) {
if(v1 >= 0) {
cov1[p] = v1, cov2[p] = -1;
if(v1 == 0) f1[p] = inf, f3[p] = 0;
else f1[p] = f0[p], f3[p] = f2[p];
if(mx[p].fi >= 0) mx[p] = mkp(v1, f2[p]);
}
if(v2 >= 0) {
cov2[p] = v2;
if(mx[p].fi > 0) mx[p] = mkp(v2, f3[p]);
}
}
void pushdown(ll p) {
if(cov1[p] == -1 && cov2[p] == -1) return;
addtag(p << 1, cov1[p], cov2[p]);
addtag(p << 1|1, cov1[p], cov2[p]);
cov1[p] = cov2[p] = -1;
}
void cover1(ll p, ll l, ll r, ll ql, ll qr, ll v) {
if(ql <= l && r <= qr) return addtag(p, v, -1);
ll mid = l + r >> 1; pushdown(p);
if(ql <= mid) cover1(p << 1, l, mid, ql, qr, v);
if(mid < qr) cover1(p << 1|1, mid + 1, r, ql, qr, v);
mx[p] = max(mx[p << 1], mx[p << 1|1]);
f1[p] = min(f1[p << 1], f1[p << 1|1]);
f3[p] = max(f3[p << 1], f3[p << 1|1]);
}
void cover2(ll p, ll l, ll r, ll ql, ll qr, ll v) {
if(ql <= l && r <= qr) return addtag(p, -1, v);
ll mid = l + r >> 1; pushdown(p);
if(ql <= mid) cover2(p << 1, l, mid, ql, qr, v);
if(mid < qr) cover2(p << 1|1, mid + 1, r, ql, qr, v);
mx[p] = max(mx[p << 1], mx[p << 1|1]);
f1[p] = min(f1[p << 1], f1[p << 1|1]);
f3[p] = max(f3[p << 1], f3[p << 1|1]);
}
pir ask(ll p, ll l, ll r, ll x) {
if(r <= x) return mx[p];
if(x < l) return mkp(-1, -1);
ll mid = l + r >> 1; pushdown(p);
return max(ask(p << 1, l, mid, x), ask(p << 1|1, mid + 1, r, x));
}
ll Get(ll p, ll l, ll r, ll x) {
if(l >= x) return f1[p];
if(r < x) return inf;
ll mid = l + r >> 1; pushdown(p);
return min(Get(p << 1, l, mid, x), Get(p << 1|1, mid + 1, r, x));
}
void modify(ll p, ll l, ll r, ll x, ll v) {
if(l == r) return mxgap[p] = v, void();
ll mid = l + r >> 1;
if(x <= mid) modify(p << 1, l, mid, x, v);
else modify(p << 1|1, mid + 1, r, x, v);
mxgap[p] = max(mxgap[p << 1], mxgap[p << 1|1]);
}
ll find(ll p, ll l, ll r, ll x, ll k) {
if(r < x || mxgap[p] <= k) return -1;
if(l == r) return l;
ll mid = l + r >> 1;
ll c = find(p << 1, l, mid, x, k);
if(c >= 0) return c;
return find(p << 1|1, mid + 1, r, x, k);
}
void upd(ll p, ll l, ll r, ll x) {
chkmin(f0[p], x), chkmax(f2[p], x);
if(l == r) return mx[p] = mkp(0, l), void();
ll mid = l + r >> 1; pushdown(p);
if(x <= mid) upd(p << 1, l, mid, x);
else upd(p << 1|1, mid + 1, r, x);
mx[p] = max(mx[p << 1], mx[p << 1|1]);
}
void build(ll p, ll l, ll r) {
cov1[p] = cov2[p] = -1, mx[p] = mkp(-1, r);
f0[p] = f1[p] = inf, f2[p] = f3[p] = mxgap[p] = 0;
if(l == r) return; ll mid = l + r >> 1;
build(p << 1, l, mid), build(p << 1|1, mid + 1, r);
}
} tr;
void solve() {
rd(n), rd(m); hxt = hyt = 0;
for(ll i = 1; i <= n; i++) {
rd(x[i]), rd(y[i]);
hx[++hxt] = x[i], hy[++hyt] = y[i];
}
sort(hx + 1, hx + 1 + hxt);
hxt = unique(hx + 1, hx + 1 + hxt) - hx - 1;
sort(hy + 1, hy + 1 + hyt);
hyt = unique(hy + 1, hy + 1 + hyt) - hy - 1;
tr.build(1, 1, hyt);
for(ll i = 1; i <= hxt; i++) vec[i].clear();
for(ll i = 1; i <= n; i++) {
x[i] = lower_bound(hx + 1, hx + 1 + hxt, x[i]) - hx;
y[i] = lower_bound(hy + 1, hy + 1 + hyt, y[i]) - hy;
vec[x[i]].pb(y[i]);
}
st.clear(), st.insert(0), st.insert(hyt + 1), top = 0;
for(ll i = 1; i <= hxt; i++) {
sort(vec[i].begin(), vec[i].end());
ll y = vec[i][0]; len = 0;
ll lim = max(0ll, m - hx[i]) / 2;
while(top) {
if(stk[top].l >= y) {
ll t = min((LL) inf, max(0ll, m - 2 * (hx[i] - hx[stk[top].x])));
t = upper_bound(hy + 1, hy + 1 + hyt, t) - hy - 1;
if(stk[top].r < hyt) s[++len] = stk[top].r;
if(t < stk[top].r) tr.cover1(1, 1, hyt, max(stk[top].l, t + 1), stk[top].r, 0), s[++len] = max(stk[top].l - 1, t);
--top;
} else if(stk[top].r >= y) {
ll t = min((LL) inf, max(0ll, m - 2 * (hx[i] - hx[stk[top].x])));
t = upper_bound(hy + 1, hy + 1 + hyt, t) - hy - 1;
if(stk[top].r < hyt) s[++len] = stk[top].r;
if(t < stk[top].r) tr.cover1(1, 1, hyt, max(t + 1, y), stk[top].r, 0), s[++len] = max(t, y - 1);
stk[top].r = y - 1; break;
} else break;
}
tr.cover2(1, 1, hyt, y, hyt, i);
stk[++top] = (Data) {y, hyt, i};
for(ll k: vec[i]) {
if(!st.count(k)) {
auto nxt = st.lower_bound(k), pre = prev(nxt);
ll u = *pre, v = *nxt;
if(u) tr.modify(1, 1, hyt, u, hy[k] - hy[u]);
if(v <= hyt) tr.modify(1, 1, hyt, k, hy[v] - hy[k]);
st.insert(k), tr.upd(1, 1, hyt, k);
}
pir tmp = tr.ask(1, 1, hyt, k); LL cst;
if(tmp.fi <= 0) cst = 2ll * (hx[i] + hy[k]);
else cst = (tmp.fi < i? 2ll * (hx[i] + hy[k]) - hx[tmp.fi] - hy[tmp.se]
: 2ll * (hy[k] - hy[tmp.se]) + hx[i]);
if(cst <= m) {
ll pos = tr.find(1, 1, hyt, k, lim);
if(pos == -1) pos = hyt;
tr.cover1(1, 1, hyt, k, pos, i);
}
}
for(ll j = len; j; j--) {
if(j < len) assert(s[j] >= s[j + 1]);
ll k = s[j];
pir tmp = tr.ask(1, 1, hyt, k);
if(tmp.fi == -1) continue;
ll r = tr.Get(1, 1, hyt, k + 1) - 1, t = tr.find(1, 1, hyt, tmp.se, lim);
chkmin(r, t == -1? hyt : t);
if(tmp.fi == i && r > k)
tr.cover1(1, 1, hyt, k + 1, r, i);
}
}
puts(tr.mx[1].fi == hxt && tr.mx[1].se == hyt? "Yes" : "No");
}
int main() {
rd(T); while(T--) solve();
return 0;
}

浙公网安备 33010602011771号