1. 程式人生 > 其它 >【總結】聯合省選題目選做

【總結】聯合省選題目選做

省選聯考 \(2021\)

P7514 [省選聯考 2021 A/B 卷] 卡牌遊戲

題目描述

Alice 有 \(n\) 張卡牌,第 \(i\)\(1 \le i \le n\))張卡牌的正面有數字 \(a_i\),背面有數字 \(b_i\),初始時所有卡牌正面朝上。

現在 Alice 可以將不超過 \(m\) 張卡牌翻面,即由正面朝上改為背面朝上。Alice 的目標是讓最終朝上的 \(n\) 個數字的極差(最大值與最小值的差)儘量小。請你幫 Alice 算一算極差的最小值是多少。

資料範圍

\(3\le n\le 10^6,1\le m<n,1\le a_i,b_i,\le 10^9\)

key idea : 極差的本質是限定值域大小,\(\forall a_i\in [l,r],r-l+1\le len\)

題解 1 (二分 + 雙指標):

首先,在最優解中我們只會翻一個字首 / 字尾,若我們翻了位置 \(1<i<n\) 且沒有翻 \(i-1\),\(i+1\) 那麼對其造成的影響要麼是最大的變大,要麼就是最小的變小。

考慮二分答案,每次 check 是否存在一種合法的翻牌方案使得極差小於等於 \(val\)

直接列舉翻牌方案,很難確定合法的翻牌的字尾。

問題的本質是,我們要找到一種翻牌方案,使得朝上的數字均在值域 \([l,r],r-l+1\le val\)

內。

對於固定的值域,在值域內的 \(a\) 我們是不需要翻的,剩下的我們只需要看要翻的位置的 \(b\) 是否在值域內。

因此,我們的值域越大越好,因此列舉值域左端點一定可以唯一確定值域最大的右端點。

將所有的 \(a\)\(b\) 排序之後,對於每次 check 列舉值域即可。

複雜度 \(O(n\log n)\)

題解 2 (排序 + 雙指標):

我們考慮將所有的 \(a\)\(b\) 排序後,預處理 \(b\) 的前後綴 \(\min,\max\) ,每次在值域上雙指標。

雙指標正確的前提:

  • 如果值域 \([l_1,r_1]\) 是合法的值域,那麼值域 \([l_2,r_2] ,l_2\le l_1 \le r_1\le r_2\)
    一定也是合法的值域。

雙指標求最優解:

  • 初始化 \(l,r\) 為包含 \(a,b\) 的極大值域。

  • 列舉值域左端點,因為要求最優解所以右指標不應當向右移動,\(r\) 移動到最左的滿足 \(l,r\) 是合法值域的位置。

複雜度 \(O(n\log n)\)

P7515 [省選聯考 2021 A 卷] 矩陣遊戲

題目描述

Alice 有一個 \(n \times m\) 的矩陣 \(a_{i, j}\)\(1 \le i \le n\)\(1 \le j \le m\)),其每個元素為大小不超過 \({10}^6\) 的非負整數。

Bob 根據該矩陣生成了一個 \((n - 1) \times (m - 1)\) 的矩陣 \(b_{i, j}\)\(1 \le i \le n - 1\)\(1 \le j \le m - 1\)),每個元素的生成公式為

\[b_{i, j} = a_{i, j} + a_{i, j + 1} + a_{i + 1, j} + a_{i + 1, j + 1} \]

現在 Alice 忘記了矩陣 \(a_{i, j}\),請你根據 Bob 給出的矩陣 \(b_{i, j}\) 還原出 \(a_{i, j}\)

資料範圍

\(1\le T \le 10,2\le n,m\le 300,0\le b_{i,j}\le 4\times 10^6\)

題解(差分約束):

key idea : 對於這種 \(2\times 2\) 的子矩陣的限制可以考慮確定第一行與第一列的狀態,從而(唯一)確定整個矩陣。

不考慮 \(a_{i,j}\) 元素大小的限制,我們考慮直接確定 \(a\) 的第一行元素和第一列元素。

這樣,我們就可以唯一確定 所有的 \(a_{i,j}\)

我們發現確定第一行元素和第一列元素和滿足 \(b_{i, j} = a_{i, j} + a_{i, j + 1} + a_{i + 1, j} + a_{i + 1, j + 1}\) 的矩陣 \(a\) 是雙射關係。

我們發現,如果我們要給第一行或第一列的某一個位置 \(+1\)\(-1\) 對整個矩陣的影響為:

  • 選一行或者一列 \(+1 - 1 + 1 - 1 ...\) 或者 \(-1 +1 -1 +1 ....\)

令目標方案為 \(c_{1,1},c_{1,2}...c_{1,m}\)\(c_{1,1},c_{2,1},...,c_{n,1}\) ,當前方案為 \(a_{1,1},a_{1,2}...a_{1,m}\)\(a_{1,1},a_{2,1},...,a_{n,1}\)

我們可以通過如下步驟將任意一個確定第一行與第一列的方案,“調整” 成目標方案:

  • 先通過將第一行與第一列進行 \(+1 - 1 + 1 - 1 ...\)\(-1 +1 -1 +1 ....\)
  • 接下來對其餘行與列對應的行 / 列進行 \(+1 - 1 + 1 - 1 ...\)\(-1 +1 -1 +1 ....\)

可以發現在第二步中,不同的行列相互不影響。

注意到,對於任意一個位置 \((i,j)\) 只會被 “調整” 兩次。

那麼 \(a_{i,j}\) 調整後的取值 \(c_{i,j}=a_{i,j} \pm P_i \pm Q_j\)

那麼有不等式組:\(0\le a_{i,j} \pm P_i \pm Q_j \le 10^6\)

然而這並不僅有我們熟知的 差分約束 ,還有 “和分約束”,並且符號我們是確定不了的,怎麼辦呢?

一種理想的確定符號的方式是棋盤黑白染色:

\[\text{行} \begin{bmatrix} + & - & + & -\\ - & + & - & +\\ + & - & + & -\\ - & + & - & +\\ \end{bmatrix} \]\[\text{列} \begin{bmatrix} - & + & - & +\\ + & - & + & -\\ - & + & - & +\\ + & - & + & -\\ \end{bmatrix} \]

但是這樣是否能夠達到任意狀態呢?

如果沒有差分約束 \(\forall x\ge 0\) 或者 \(\forall x\le 0\) 的限制,其是很好證明的(因為不同的行和列是獨立的)。

引理:對於一組形如 \(x_i-x_j\le a_k\) 的不等式組存在一組解,那麼一定可以用差分約束求解出一組 \(\forall x \ge 0\)\(\forall x \le 0\) 的一組解。

黑白染色後,可以保證其符號是不同的。

複雜度 \(O(nm(n+m))\)

P7516 [省選聯考 2021 A/B 卷] 圖函式

題目描述

對於一張 \(n\) 個點 \(m\) 條邊的有向圖 \(G\)(頂點從 \(1 \sim n\) 編號),定義函式 \(f(u, G)\)

  1. 初始化返回值 \(cnt = 0\),圖 \(G' = G\)
  2. \(1\)\(n\) 按順序列舉頂點 \(v\),如果當前的圖 \(G'\) 中,從 \(u\)\(v\) 與從 \(v\)\(u\) 的路徑都存在,則將 \(cnt + 1\),並在圖 \(G'\) 中刪去頂點 \(v\) 以及與它相關的邊。
  3. \(2\) 步結束後,返回值 \(cnt\) 即為函式值。

現在給定一張有向圖 \(G\),請你求出 \(h(G) = f(1, G) + f(2, G) + \cdots + f(n, G)\) 的值。

更進一步地,記刪除(按輸入順序給出的)第 \(1\)\(i\) 條邊後的圖為 \(G_i\)\(1 \le i \le m\)),請你求出所有 \(h(G_i)\) 的值。

資料範圍

\(1\le n\le 10^3,1\le m\le 2\times 10^5\)

題解 ( \(O(n^3)\) 動態加邊傳遞閉包 )

我們將計算 \(f(u,G)\) 的過程重新描述一下:

  • 初始化返回值 \(cnt = 0\),圖 \(G' = G\) ,集合 \(S_{u,G} = \emptyset\)
  • \(1\)\(n\) 按順序列舉頂點 \(v\),如果當前的圖 \(G'\) 中,從 \(u\)\(v\) 與從 \(v\)\(u\) 的路徑都存在,則將 \(cnt + 1\)\(S_{u,G} \gets S_{u,G} \cup <u,v>\) , 其中 \(<u,v>\) 是有序點對,並在圖 \(G'\) 中刪去頂點 \(v\) 以及與它相關的邊。
  • 對於任一有序點對 \(<u,v>\in S_{u,G}\) ,一定有 \(v\le u\)
  • \(f(u,G)=cnt=|S_u,G|\)
  • 因為任意兩個不同的 \(u\) ,\(S_{u,G}\) 不交,所以有 \(h(G)=|\bigcup_{1\le u\le n} S_{u,G}|\)
  • 定義集合 \(H(G)=\bigcup_{1\le u\le n} S_{u,G}\)

考慮點對 \(<u,v> \in H(G)\) 的條件:

  • 當且僅當圖 \(G\) 中存在路徑 \(u\to v\) 滿足路徑上編號最小的點的編號為 \(v\) ,且存在路徑 \(v\to u\) 滿足路徑上編號最小的點的編號為 \(v\)

動態刪邊不是一個好做的條件,我們將其轉化為倒著加邊。

\(f[x][y]\) 為點 \(x\) 到點 \(y\) 路徑上編號最小的點的編號最大是多少。

動態加邊更新這個陣列即可。 複雜度 \(O(n^2m)\) 可以拿到 \(44\) 分。

但是這個做法接著往下想是很沒用前途的。

令圖 \(G_i'\gets G_{m-i+1}\)

我們考慮一個點對 \(<u,v>\) 哪些集合 \(H(G_i')\) 中。

我們考慮固定 \(G\) 去求 \(H(G)\) 的暴力做法:

  • 從後往前列舉 \(v\) 每次列舉一個 \(u\) 查詢 \(v\) 是否可以到達 \(u\) , \(u\) 是否可以到達 \(v\)

而對於 “只有加邊的動態傳遞閉包問題” 中,我們有一個建圖技巧:

對於第 \(i\) 條插入的邊 \((u_i,v_i)\) ,建邊 \((u_i,v_i,i)\)

那麼每次相當於 \(u\)\(v\) 路徑上最大的邊權最小是多少。

對於該問題,我們採用類似地建邊方法,即將邊 \((u_i,v_i)\) 建成 \((u_i,v_i,i)\)

那麼跑動態加點的 \(\text{floyd}\) 即可(複雜度 \(O(n^3)\) ),卡卡常可以過。

卡常小技巧(可以節約近一半的常數):

for (int x = n; x >= 1; x--) {
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= (i>x?x:n); j++)
			f[i][j] = std::max(f[i][j],std::min(f[i][x],f[x][j]));
}