XYD1004CSPS(找個好日子把T4的史赤了)

chenwenmo發表於2024-10-09

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\) 的資料範圍。

相關文章