題解 csp s2021 T2 括號序列

2022-09-12 13:00:31 字數 3213 閱讀 6294

本文首先發布於個人部落格,不定期更新。推薦去我的部落格閱讀,效果更佳。

因為 csp-s 考的太慘了,所以一直想著回頭把前三題都ac了,結果一拖拖到現在才做完(

還有就是動態規劃好難(wtcl)

本題題意較複雜,建議直接去看原題(題目鏈結)

總的來說,合法的括號序列一共分兩類:

包含型:(), (a),(s),(as),(sa)

並列型: ab ,asb

很容易發現合法的括號序列兩端必然分別是左右括號,而且合法的括號序列最小長度為2

資料範圍可知這題大概需要乙個 \(o(n^3)\) 的方法

這道題一看就是區間dp(假如你不知道什麼是區間dp就先去學一下,等以後再看這篇文章吧)

第一直覺估計都是狀態 \(f[i][j]\) 表示 \([i,j]\) 這段區間內的數量,然後轉移方程也都比較好想,例如:

\[\left. \begin

\\ \\\end \right.

\]( \(s[i][j]\) 表示 \([i:j]\) 能否完全由不超過 k 個連續的*或者?組成,放在逗號右面表示只有當滿足這個條件時才計算)

(as) 只要列舉一下 s 就行( \(p\) 為 a 的右端點):

\[^f[i][p],\space s[p+1][j-1]=true }

\](sa) 與 (as) 差不多就不再提了。

但是在計算 asb 的貢獻的時候,像我們剛剛那樣設計狀態就會列出錯誤的方程( \(p\) 為 a 的右端點):

\[f[i][j]+=\sum_^\sum_^ f[i][p]\times f[q][j],\space s[p+1][q-1]=true

\]這個式子可以理解成列舉 a 的右端點 \(p\) 和 b 的左端點 \(q\) 來間接列舉 s ,從而轉移 asb,做個特判就可以連 ab 一起處理了。

但是這樣的轉移方程為什麼是錯的呢,我們看下面這樣的括號序列:

()()()

當你在列舉到 a = [1:2] 時會計算一遍這種情況,而 a = [1:4] 的時候又會被計算一遍

於是你發現你算重了

所以我們要想別的辦法來避免算重。比較容易想到的就是我每次在左邊的 a 只列舉包含型的結構,也就是說在剛剛的例子中列舉 a 只會列舉 [1:2] =()這種包含型的,而不會列舉 [1:4] =()()這種並列型的,這樣你每種拆法就會只被計算一次了。

重新設計一下狀態,\(f[i][j]\) 表示該區間內包含型序列的數量,\(g[i][j]\) 表示並列型序列的數量。

重新看一下原來的轉移方程,你會發現 (a),(s) 的轉移方程根本不需要改,(as) 的轉移方程只是把後面的 \(f[i][p]\) 改成 \(f[i][p]+g[i][p]\) 即可

仿照原來的思路,asb 的轉移方程如下:

\[g[i][j] = \sum_^\sum_^ f[i][p] \times (f[q][j]+g[q][j]),\space s[p+1][q-1] = true

\]**如下:(為了清晰直觀,刪去取餘的過程)

for(int p=i+1; p <= j-2; p++)

}

按這個思路寫對了應該就能拿到 65pts 了,現在離 ac 還差乙個優化

效能的瓶頸全都在 asb 的 \(o(n^4)\) 上,所以觀察 asb 的式子,此時 \(i\) 和 \(j\) 都可以當做定值。

後面的 \(f[q][j]+g[q][j]\) 的值與 \(p\) 無關,因此我們可以把 \(f[i][p]\) 從最內層的 \(\sum\) 裡提出來。

所以現在只考慮內層的 \(\sum\):

\[\sum_^(f[q][j]+g[q][j]),\space s[p+1][q-1] = true

\]這時如何優化就很容易想到了:字首和

我們設乙個新陣列 \(h\),先不去考慮*的條件:

\[h[p]=\sum_^f[t][j]+g[t][j]

\]我們可以在讀入之後預處理出每乙個位置往後延伸的*或者?的最長的長度,記為 \(b[i]\)

每次迴圈 \(i\) 的時候預處理 \(h\) 陣列的值,因為 \(l\) 遞增迴圈所以不用擔心 \(f[q][j]\) 還沒有求的問題,這樣我們可以用 \(h[min(j, p+b[p+1]+1)] - h[p]\) 代替原來的內層的 \(\sum\)(其實建議預處理時直接處理出向後延伸的最大下標,應該會方便一點,但我懶得改了)

最終**(**醜,請見諒):

#include #include using namespace std;

const int maxn = 550;

const int mod = 1000000007;

typedef unsigned long long ull;

int inline add(int a, int b)

int n, k;

char a[maxn];

int f[maxn][maxn]; // (...), 後面注釋裡用 f 代替

int g[maxn][maxn]; // (...)...(...) 用 g 代替

bool s[maxn][maxn]; // s[i][j] 表示能否構成連續的而且 個數不超過 k 的 *

int h[maxn], b[maxn];

int main()

b[i] = j-i;

}for(int l=2; l <= n; l++)

if(s[i+1][j-1] && l-2 <= k) f[i][j] = 1; // (s)

f[i][j] = add(f[i][j], add(f[i+1][j-1], g[i+1][j-1])); // (f) 或 (g), 對應題目要求的 (a)

for(int p=i+1; p < j-1; p++)

for(int p=j-1; p > i+1; p--)

// ab 或者 asb

h[i+1] = 0; // 字首和優化

for(int p=i+2; p <= j; p++)

for(int p=i+1; p <= j-2; p++)}}

printf("%d", add(f[1][n], g[1][n]));

return 0;

}

csp s測試41 T2 影子

1 並查集 可以並查集 考慮對點權的限制。嘗試逐點列舉點權,向點權大於等於自己的節點擴充套件,計算最大路徑。優化 瓶頸在於還是有很多重複的。上述的每個節點擴充套件後形成的連通塊點集成為乙個集合,從大點權到小點權只要集合拓展。維護集合 考慮並查集 點權排序,維護集合內最長鏈即可。nlog includ...

Csp S 2020 T4 貪吃蛇 題解

這道題我調了好久 細節非常多。首先,看完題目後,可以發現這是一道博弈問題。那麼接下來,我們假設最強蛇為 x xx 最弱蛇為 y yy 那麼這裡就有兩個結論 如果 x xx 吃了 y yy 不是最弱的蛇,則 x xx 必吃 y yy 證明 假設當前第二強的蛇為 a aa 第二弱的蛇為 b bb 那麼 ...

NOIPD2T2 寶藏 題解

填坑,史前巨坑。題意 對於一張圖,確定乙個點為根,構建乙個生成樹。求代價最小值。代價的定義 樹中每一條邊的權值與較淺點深度的乘積 之和。考場上沒有想清楚就草草碼了乙個prim然後交了,但是因為你代價和深度有關,所以貪心地prim是錯誤的。因為 n 很小,這應當引導我們想到狀壓。套路 答案與深度有關,...