24.07 做題記錄

ChthollyNS發表於2024-07-14

24.7 降維技巧

約定

diff=1 水題 黃以下 一眼切

diff=2 easy 下位綠及黃 能切

diff=3 medium 特殊的黃;綠;較簡單的藍 可能需要題解提供一步

diff=4 hard 藍色+ 需要題解

diff=5 不可做題 紫色+ 做不了

字首和/差分

P1115 最大子段和

diff:1

字首和板子。

code

P3406 海底高鐵

diff:1

注意讀題。字首和板子。

看好資料範圍

code

P2671 [NOIP2015 普及組] 求和

diff:2.5 tag:字首和 性質

難點在於讀題。

\((x,y,z)\) 表示的 編號。如果讀對了題會發現 \(y\) 沒用。

\(2y=x+z\) 可以發現 \(x,z\) 奇偶性相同。將奇偶不同的編號分開排序。

按照顏色第一關鍵字,大小第二關鍵字排序並字首和即可。

"寫的是題解的三倍,還多一支 \(\log\),附贈一堆細節"

code

P1314 [NOIP2011 提高組] 聰明的質監員

tag:二分 字首和

你說得對,但是當答案可能超過 int 時最小值初始化應為 LONG_LONG_MAX 而不是 INT_MAX。

二分 \(W\) 對於每個不同的 \(W\) 處理一次 \(w_i\ge W\) 的字首和,複雜度就是對的了。

注意二分返回 true/false 的條件,當 \(s>ans\) 時 返回 false

code

P1083 [NOIP2012 提高組] 借教室

我會線段樹。

題意即為 \((+,\min)\) 線段樹。

注意線段樹的 add 函式改沒改

code

P2367 語文成績

差分板子。

code

P2882 [USACO07MAR] Face The Right Way G

diff:3 tag:貪心 細節

為什麼從前往後看到方向朝後的就轉回去是對的?

我們注意到把一個點轉兩次等於啥也沒幹 可以模擬得出貪心正確性。

最佳化簡單于貪心。xor 差分維護當前是否轉向即可。

注意程式碼細節,差分的下一個數是 j+i 沒有 +1!

code

高維字首和/差分

P2004 領地選擇

diff:1

板子題。但是寫對板子。

\[sum_{i,j}=sum_{i,j-1}+sum_{i-1,j}-sum_{i-1,j-1}+a_{i,j}\\ sum_{x2,y2}-sum_{x1-1,y2}-sum_{x2,y1-1}+sum_{x1-1,y1-1} \]

code

P3397 地毯

diff:1

板子。

code

P2280 [HNOI2003] 鐳射炸彈

diff:1.5

座標有0 座標有0 座標有0 座標有0。記得對所有的陣列偏移 \(1\),不然 91 pts。

code

P1719 最大加權矩形

diff:1

板子。建議你谷課程題單減少板子題數量。

code

P3017 [USACO11MAR]Brownie Slicing G

diff:3.5 tag:貪心 性質 二分 字首和

USACO 首先考慮貪心

二分答案,對於每一次 check,我們可以貪心。

具體的,先滿足列的條件。如果該列在滿足 \(\ge x\) 情況下無法 \(\ge b\) 塊,那麼加一行,注意每一塊的範圍是以 (上次分割行,上次分割列) 和 (列舉點行,列舉點列) 形成的長方形而不是一行連著取。是否滿足條件即是否 \(\ge a\)

字首和即可。

因為少寫 else 多交兩發。

code

[ARC100E] Or Plus Max

diff:4 tag:sosdp 高維字首和

牛逼題。參考連結1 參考連結2

考慮對每個 \(k\) 分開計算,即令 \(A_k=\max_{i\operatorname{or} j=k} a_i+a_j\),則 \(ans_k=\max_{i=1}^k A_i\)又注意到上式即 \(A_k=\max_{i,j\subseteq} a_i+a_j,ans_k=\max_{i=1}^k A_i\) 這是由於或運算的不減性。該轉化是進行 sosdp 的前提。

事實上該問題可以列舉子集,\(O(3^n)\),事實上我們可以非容斥計算字首和。

e.g. 三維字首和

for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
        for(int k = 1; k <= n; k++) 
            a[i][j][k] += a[i - 1][j][k];
for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
        for(int k = 1; k <= n; k++)
            a[i][j][k] += a[i][j - 1][k];
for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
        for(int k = 1; k <= n; k++)
            a[i][j][k] += a[i][j][k - 1];

sosdp 是一個每一維度長度都為 2 的高維 dp。

for(int i=0;i<=n-1;i++)
    for(int j=0;j<=(1<<n)-1;j++)
        if((1<<i)&j)
            m[j]=m[j]+m[j-(1<<i)];

這段程式碼中 \(j\)\(j-2^i\) 的上一層,從 0 開始,建議背過

從 dp 角度理解:

定義 \(f_{i,r}\) 表示當前處理到二進位制最後 \(i\) 位,狀態為 \(r\) 的情況,當列舉 \(i+1\) 位時,若 \(r\) 即當前狀態為 1,就從 \(f_{i,r},f_{i,r-2^i}\) 轉移,分別表示有/無該位的情況併合並,否則從 \(f_{i,r}\) 轉移即可。實現上,我們滾動掉第一維。

求超集僅需在 if 中加入一個 ! 取反即可。

code

樹上差分

P3128 [USACO15DEC] Max Flow P

diff:2

我會樹剖。

code

P3258 [JLOI2014] 松鼠的新家

diff:2

我也會樹剖。

code

P2680 [NOIP2015 提高組] 運輸計劃

todo

P1600 [NOIP2016 提高組] 天天愛跑步

todo

離散化

P1097 [NOIP2007 提高組] 統計數字

diff:1

板子。

code

P1955 [NOI2015] 程式自動分析

diff:2.5 tag:並查集 離散化

diff=2.5 而不是 2 的原因是寫掛了若干發。

我們只需要判斷是否在不相等時能否滿足,原因是顯然的——等式具有傳遞性,所以我們事實上只需要維護相等這一個並查集即可。

離散化寫了要用。並查集要寫 f[i]=i

fun fact:洛谷 #2 #10 放反了

code