Loading

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;
}
posted @ 2026-01-15 21:44  Sktn0089  阅读(12)  评论(0)    收藏  举报