省選集訓2022 模擬賽6

2022-09-21 00:24:09 字數 1657 閱讀 9046

題目描述

定義長度為 \(n\) 的好串 \(s\) 滿足:

給你長度為 \(n\) 的序列 \(a\) 和 \(v\),分別表示原序列和價值序列。你每次可以選擇乙個原序列中的好串,將其刪除之後剩下的串會前後拼接。設這次刪除的長度是 \(l\),那麼會得分 \(v_l\),問最大得分,不一定要把原序列刪完。

\(n\leq 400,|v_i|\leq 10^5,a_i\leq10^9\)

解法

考慮求出 \(f[l][r]\) 表示把 \([l,r]\) 刪完的最大得分,然後再用一維 \(dp\) 就可以拼出答案。

好串其實就是先以 \(1\) 的斜率上公升、再以 \(1\) 的斜率下降的尖角形。發現還是不好做,我們可以考慮把它們拆成上公升部分和下降部分,然後拼起來,拆分後的問題還是可以用 \(dp\) 解決。

具體來說我們設 \(up[l][r]\) 表示獲取以 \(a_l\) 開頭 \(a_r\) 結尾的上公升段的最大價值,\(dn[l][r]\) 表示獲取 \(a_l\) 開頭 \(a_r\) 結尾的上公升段的最大價值,那麼轉移列舉 \(i\) 使得 \(a_i+1=a_r/a_i-1=a_r\):

\[up[l][r]\leftarrow up[l][i]+f[i+1][r-1]

\]\[dn[l][r]\leftarrow dn[l][i]+f[i+1][r-1]

\]那麼如何把他們拼起來呢?我們列舉最高點 \(i\) 使得 \(a_i\geq a_l\and a_i\geq a_r\),那麼好串的長度一定是 \(2\cdot a_i-a_l-a_r\),很容易寫出轉移:

\[f[l][r]\leftarrow up[l][i]+dn[i][r]+v[2\cdot a_i-a_l-a_r+1]

\]但是 \(a_l,a_r\) 也可以不在乙個好串中,這時候我們需要列舉分界點將他們分開:

\[f[l][r]\leftarrow f[l][i]+f[i+1][r]

\]此外只有上公升段和下降段需要單獨討論一下,時間複雜度 \(o(n^3)\)

總結

設計多個 \(dp\) 狀態,互相轉移的方法是很值得思考的。其實我感覺它的原理還是__分步思想__,把乙個較為複雜的問題拆分成若干個部分解決,這些部分中可能又蘊含子問題。

#include #include using namespace std;

const int m = 405;

const int inf = 0x3f3f3f3f;

int read()

while(c>='0' && c<='9')

return x*f;

}int n,a[m],v[m],dp[m],f[m][m],up[m][m],dn[m][m];

void upd(int &x,int y)

signed main()

if(a[l]>=a[r])

upd(f[l][r],dn[l][r]+v[a[l]-a[r]+1]);

if(a[l]<=a[r])

upd(f[l][r],up[l][r]+v[a[r]-a[l]+1]);

} for(int i=1;i<=n;i++)

printf("%lld\n",dp[n]);

}

省選集訓2022 模擬賽8

題目描述 給定乙個 n times m 的 01 矩陣,對於矩陣的每乙個位置,你需要對於這個位置上的值反轉,然後求出這個矩陣的秩的變化 0 可以將這個矩陣看成 n 個大小為 0,2 m 的數,秩就是它們線性基的大小。n,m leq 1000 解法 我們判斷求出原先的 n 個向量在原來的線性基中是可以...

省選集訓2022 模擬賽10

題目描述 給定 n 個元素,每個元素有兩個屬性值 a i,b i 我們可以將其以任意順序排列,要最大化下式 min a i i cdot k max b i i cdot k n leq 10 5,a i,b i,kn leq 10 9 解法 應該是遇到困難退大火才對,直接使用列舉法,考慮列舉 m ...

省選集訓2022 模擬賽11

題目描述 n 個城市構成一棵樹,現在要求在一些城市中設定監測點,使得每個城市可以通過到監測點的距離區分出來 不同可以知道是到哪個監測點的距離,可以模擬為樹上的座標 給定 q 次修改,每次斷開邊 u,v 再連上邊 x,y 然後求出最小設定的監測點數目。解法 無根樹問題考慮定根,我們先列舉根強制根選取,...