T1 序列 [貪心,模擬]
Description
給定一個序列,每次可以將一個數減一,求讓所有數字互不相同的最小運算元,\(0\) 除外,也就是 \(0\) 可以相同。
Solution
貪心地,從大到小考慮每個數,都只需要減到第一個沒有數出現的數。
開個桶從大到小累計答案即可。
T2 XYD [構造]
Description
給定 \(n\),構造一個長度不超過 \(6000\) 且僅含 X Y D 三個字母的字串,使得子序列 XYD 恰好出現 \(n\) 次。
Solution
對於每個 X,假設後面有 \(k\) 個 YD,那麼它們產生的貢獻是 \(\sum_{i=1}^k i\)。
於是我們可以把 \(n\) 拆成許多個數的階加即可。
可以選擇最大到 \(2000\) 的階加,如果過大那麼字串長度可能超過 \(6000\)。
T3 大 [狀壓DP]
Description
初始序列 x 和 y,有 \(m\) 個操作序列 a,每次操作可以把 x 每一個元素變為 \(\max(x_i,a_i)\) 或對 y 進行同樣操作。
最大化 \(\sum \max(x_i,y_i)\)。
Solution
考慮每一位,假設它的操作是獨立的,那麼我們肯定貪心地每一位選最大的是最優的。
但是一個序列只能對 x 和 y 其中一個操作,不能同時操作兩個,所以這便涉及到最優分配。
為了方便處理,我們可以把取最大值的操作轉換為賦值操作,也就是取一堆最大值,相當於把一個最大的直接賦值給它,考慮哪些位上被賦值,哪些位保留。
綜上,要考慮到賦值哪些位,還要考慮到賦值給 x 或 y,於是我們選擇 dp 求解。
設 \(f_x(i,j)\),\((1\le i\le m)\),\((0\le j\le 2^{2n})\) 表示考慮前 \(i\) 個操作序列,現在是第 \(i\) 個,賦值狀態為 \(j\),且賦值給 x,所能取到的最大答案。\(f_y(i,j)\) 同理。
顯然直接轉移狀態數是會炸的。
但是,每一位我們只需要考慮前 \((n+1)\) 大的,因為最壞的情況就是,x 想要的都被 y 搶走更優,那麼 y 最多能佔用 \(n\) 個,\(x\) 選第 \((n+1)\) 個即可。然後又因為一共有 \(n\) 位,所以狀態數最多隻有 \(O(n(n+1))\)。dp 的複雜度為 \(O(n(n+1)2^{2n})\),非常正確的時間複雜度。
然後空間上,把第一維滾掉即可。
dp 虛擬碼:
for(int i : 1 ~ m){
for(int j : 1 ~ n){
if(id[i][j] <= n + 1){ // 每位只考慮前 (n + 1) 個
for(int k : 1 ~ 2^2n){
if(第 j 位被選了){
fx[k] = max(fx[k], fx[k ^ (1 << (j - 1))] + a[i][j]);
}
}
for(int k : 1 ~ 2^2n){
if(第 j 位被選了){
fy[k] = max(fy[k], fy[k ^ (1 << (j + n - 1))] + a[i][j]);
}
}
}
}
// 合併答案 滾動陣列
fx[] = fy[] = max(fx[], fy[]);
}
// 把沒賦值的加上原來的值
for(int i : 1 ~ 2^2n){
for(int j : 1 ~ n){
if(第 j 位沒被賦值){
fx[i] += x[j];
}
}
for(int j : n+1 ~ 2n){
if(第 j 位沒被賦值){
fx[i] += y[j];
}
}
}
ans = max{fx[]}; // 用 fy[] 也行,因為已經合併了
Summary
DP 好題,把取 \(\max\) 轉化為了賦值,並巧妙利用了題目 \(n\) 的資料範圍。