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
板子題。但是寫對板子。
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