P9870 [NOIP2023] 雙序列擴充 題解

小超手123發表於2024-04-05

題意:

稱某個序列 \(B = \{b_1,b_2,\cdots,b_n\}\) 是另一個序列 \(A = \{a_1,a_2,\cdots,a_m\}\)擴充當且僅當存在正整數序列 \(L = \{l_1,l_2,\cdots,l_m\}\),將 \(a_i\) 替換為 \(l_i\)\(a_i\) 後得到序列 \(B\)。例如,

  • \(\{1,3,3,3,2,2,2\}\)\(\{1,3,3,2\}\) 的擴充,取 \(L = \{1,1,2,3\}\)\(\{1,2,1,3\}\)
  • \(\{1,3,3,2\}\) 不是 \(\{1,3,3,3,2\}\) 的擴充,\(\{1,2,3\}\) 不是 \(\{1,3,2\}\) 的擴充。

小 R 給了你兩個序列 \(X\)\(Y\),他希望你找到 \(X\) 的一個長度為 \(l_0 = 10^{100}\) 的擴充 \(F = \{f_i\}\) 以及 \(Y\) 的一個長度為 \(l_0\) 的擴充 \(G = \{g_i\}\),使得任意 \(1 \le i , j \le l_0\) 都有 \((f_i - g_i)(f_j - g_j) > 0\)。由於序列太長,你只需要告訴小 R 是否存在這樣的兩個序列即可。

為了避免你扔硬幣矇混過關,小 R 還給了 \(q\) 次額外詢問,每次額外詢問中小 R 會修改 \(X\)\(Y\) 中若干元素的值。你需要對每次得到的新的 \(X\)\(Y\) 都進行上述的判斷。

詢問之間是獨立的,每次詢問中涉及的修改均在原始序列上完成。

分析:

\((f_i - g_i)(f_j - g_j) > 0\) 這個條件可以看作要麼 \(X\) 的擴充套件均比 \(Y\) 的擴充套件小,要麼大。因此只需要考慮 \(X\) 的擴充套件均比 \(Y\) 的擴充套件小即可。

對於目前在 \(X\) 序列中匹配到 \(i\),在 \(Y\) 序列中匹配到 \(j\)。可以從 \((i,j)\) 轉移到 $(i+1,j) \ (x_{i+1}<y_{j}) \(,\)(i,j+1) \ (x_{i} < y_{j+1})$ 以及 \((i+1,j+1) \ (x_{i+1}<y_{j+1})\)。簡單 dp 一下,時間複雜度為 \(O(Tnm)\) 的。

因此可以將題意轉換成 \(A_{i,j} =[x_{i} < y_{j}]\)。判斷能否從 \((1,1)\) 走到 \((n,m)\)

特殊性質:

特殊性質:對於每組詢問(包括初始詢問和額外詢問),保證 \(x_1 < y_1\),且 \(x_n\) 是序列 \(X\) 唯一的一個最小值,\(y_m\) 是序列 \(Y\) 唯一的一個最大值。

提示我們可以從最小值最大值來尋找突破口。

顯然如果 \(X_{min} < Y_{min}\) 時,\(A_{X_{min},j}=1\);否則 \(A_{i,Y_{min}}=0\)(這一列被堵住了,肯定不合法)。

同理 \(X_{max} < Y_{max}\) 時,\(A_{i,Y_{max}}=1\);否則 \(A_{X_{max},j}=0\)(這一行被堵住了,肯定不合法)。

那麼合法的一個必要條件是 \(X_{min} < Y_{min} \land X_{max} < Y_{max}\)

保證這個條件後,那麼最後一行和最後一列均為 \(1\)

分別找到 \(X,Y\) 的前 \(n-1,m-1\) 的最小值,如果 \(X_{min} < Y_{min}\)

P9870 [NOIP2023] 雙序列擴充 題解

可以走到 \(X_{min}\) 這一行,再走到終點,這樣我們就縮小了邊框。\(X_{max} < Y_{max}\) 同理。需要注意的是如果兩個條件均不滿足,就說明一定不合法。

因此可以記 check1(int x, int y) 表示判斷 \((1,1)\) 是否能走到第 \(x\) 行或第 \(y\) 列。

時間複雜度 \(O(T(n+m))\)

一般情況:

實際上一般情況只是說明最開始的 \(X_{min}\)\(Y_{max}\) 不一定在最後一行和最後一列。我們直接找到 \(X_{min}\)\(Y_{max}\),然後就變成了一個左上角和右下角的子問題。再記 check2(int x, int y, int n, int m) 表示目前能走到第 \(x\) 行和第 \(y\) 列,判斷其是否能走到 \((n,m)\)

使用 \(check1\) 去計算左上角,\(check2\) 去計算右下角即可。

時間複雜度同樣為 \(O(T(n+m))\)

程式碼:

#include<bits/stdc++.h>
#define N 500005

using namespace std;
int c, n, m, Q;
int X[N], Y[N];
struct node {
	int id, val;
}A_pre_max[N], A_pre_min[N], A_suf_max[N], A_suf_min[N], B_pre_max[N], B_pre_min[N], B_suf_max[N], B_suf_min[N];
node add1(node x, node y) {
	node z;
	z.val = min(x.val, y.val);
	if(z.val == x.val) z.id = x.id;
	else z.id = y.id;
	return z;
}
node add2(node x, node y) {
	node z;
	z.val = max(x.val, y.val);
	if(z.val == x.val) z.id = x.id;
	else z.id = y.id;
	return z;
}
bool check1(int x, int y) { //判斷[1,1]是否能走到第x行或第y列 
	if(x == 1 || y == 1) return 1;
	node xmin = A_pre_min[x - 1], xmax = A_pre_max[x - 1];
	node ymin = B_pre_min[y - 1], ymax = B_pre_max[y - 1];
	if(xmin.val < ymin.val) return check1(xmin.id, y);
	if(xmax.val < ymax.val) return check1(x, ymax.id);
	return 0;
}
bool check2(int x, int y, int n, int m) { //目前能走到第x行和第y列,判斷其是否能走到[n,m] 
	if(x == n || y == m) return 1;
	node xmin = A_suf_min[x + 1], xmax = A_suf_max[x + 1];
	node ymin = B_suf_min[y + 1], ymax = B_suf_max[y + 1];
	if(xmin.val < ymin.val) return check2(xmin.id, y, n, m);
	if(xmax.val < ymax.val) return check2(x, ymax.id, n, m);
	return 0;
}
bool Sol(int n, int m, int X[], int Y[]) {
	if(X[1] > Y[1]) return 0;
	A_pre_max[1] = A_pre_min[1] = ((node){1, X[1]});
	B_pre_max[1] = B_pre_min[1] = ((node){1, Y[1]});
	A_suf_max[n] = A_suf_min[n] = ((node){n, X[n]});
	B_suf_max[m] = B_suf_min[m] = ((node){m, Y[m]});
	for(int i = 2; i <= n; i++) {
		A_pre_min[i] = add1(A_pre_min[i - 1], (node){i, X[i]});
		A_pre_max[i] = add2(A_pre_max[i - 1], (node){i, X[i]});
	}
	for(int i = n - 1; i >= 1; i--) {
		A_suf_min[i] = add1(A_suf_min[i + 1], (node){i, X[i]});
		A_suf_max[i] = add2(A_suf_max[i + 1], (node){i, X[i]});
	}
	for(int i = 2; i <= m; i++) {
		B_pre_min[i] = add1(B_pre_min[i - 1], (node){i, Y[i]});
		B_pre_max[i] = add2(B_pre_max[i - 1], (node){i, Y[i]});
	}
	for(int i = m - 1; i >= 1; i--) {
		B_suf_min[i] = add1(B_suf_min[i + 1], (node){i, Y[i]});
		B_suf_max[i] = add2(B_suf_max[i + 1], (node){i, Y[i]});
	}
	if(A_pre_min[n].val >= B_pre_min[m].val || A_pre_max[n].val >= B_pre_max[m].val) return 0;
	return check1(A_pre_min[n].id, B_pre_max[m].id) && check2(A_pre_min[n].id, B_pre_max[m].id, n, m);
}
int AA[N], BB[N];
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> c >> n >> m >> Q;
	for(int i = 1; i <= n; i++) cin >> X[i], AA[i] = X[i];
	for(int i = 1; i <= m; i++) cin >> Y[i], BB[i] = Y[i];
	cout << (Sol(n, m, X, Y) || Sol(m, n, Y, X));
	while(Q--) {
		int kx, ky;
		cin >> kx >> ky;
		for(int i = 1; i <= n; i++) X[i] = AA[i];
		for(int i = 1; i <= m; i++) Y[i] = BB[i];
		for(int i = 1, p, v; i <= kx; i++) {
			cin >> p >> v;
			X[p] = v;
		}
		for(int i = 1, p, v; i <= ky; i++) {
			cin >> p >> v;
			Y[p] = v;
		}
		cout << (Sol(n, m, X, Y) || Sol(m, n, Y, X));
	}
	return 0;
}

相關文章