AHOI2022 山河重整

2022-10-11 07:18:10 字數 2812 閱讀 1645

今年的獨立命題除了福建都很一可賽艇啊!

首先有個經典結論是,如果選出的子集 \(s\) 合法,那麼 \(\forall i, \sum_ j \geq i\)。

那麼可以得到乙個 \(o(n^2)\) 的 dp。定義 \(dp_\) 為在前 \(i\) 個數中,可以構出 \([1,j]\) 內的所有數(第二維與 \(n\) 取最小值),直接轉移即可。

陷入困境。一方面這個 dp 的形式不好優化,另一方面容易發現這個 dp 的劣勢在於第二維進行了壓縮,我們無法得知準確的資訊。

那麼考慮算不合法的方案數。我們在每乙個方案中第乙個無法被表示的位置 \(i+1\) 計數。容易發現此時小於等於 \(i\) 的數的和就是 \(i\)。

那麼定義 \(f_i\) 為集合 \(\\) 有多少子集使得和為 \(i\),且可以表示 \([1,i]\) 內的所有數。遺憾的是在這裡我們陷入困境,一方面是直接樸素 dp 和上面的形式毛區別也沒有,另一方面可以表示 \([1,i]\) 內的所有數這個限制非常的離譜。

但是,注意到和為 \(i\) 這個限制,並且選出的數都不相同,容易發現我們選出來的數是 \(o(\sqrt n)\) 級別的,並且避免了資訊的壓縮。那我們轉換計數視角,下面給出乙個例項:

容易發現我們選擇了 \(s=\\),和為 \(12\)。其中大小不同的行數量級別為 \(o(\sqrt)\)。(也可以按整數拆分選擇的不同數個數這個角度來考慮。)

那麼考慮我們之前 dp 的實質,我們是每次加入一列。那麼這裡我們按行來考慮。發現這個圖有如下特點:

根據實際意義容易發現到上面的結論。

注意到我們直接計算 \(f_i\) 仍然困難,因為可以表示 \([1,i]\) 內的所有數這個限制確實比較難處理。我們考慮容斥,先算出乙個不考慮這個限制的 \(f_i\)。

如何算這個不帶限制的 \(f_i\) 呢?容易發現每個大小的行可以出現任意次,類似於完全揹包,區別在於帶了類似「如果要選 \(i\),必須先選 \([1,i-1]\) 內的每個數至少一次」限制。那麼我們考慮從大的數選起,每次列舉到 \(i\) 的時候為了保證更上面的方案是合法的,我們先選擇乙個 \(i\),然後再更新。

// 此時 f[0] 不等於 1

for(int i=n;i;--i)

f[0]=1;

那麼對每個 \(i\) 考慮容斥掉不合法的方案數,得到正確的 \(f_i\)。

先考慮對 \(i\) 有貢獻的 \(j\)。首先為了方便計數,並且為了滿足定義,顯然我們後來選的最小的數為 \(j+2\)(因為我們要滿足選之後,\(j+1\) 是最小的不能被構出的數,並且和為 \(i\))。那麼不難發現對 \(i\) 有貢獻的 \(j\),滿足 \(2j+2 \leq i\)。

注意到這個點,我們在處理 \(f_\) 的時候,可以先處理 \(f_}\),然後繼續處理後面的東西。

那麼我們已經知道了前面一半的 \(f\),要求後面一半。

這個時候繼續考慮加入每一行之類的想法就非常的沒有前途(主要是我沒找到)。注意到我們現在要的是,通過選擇 \([j+2,i]\) 裡面的數,使得和為 \(i-j\)。我們考慮劃分數相關的想法。

假設沒有選擇的數限制(但是還是有選擇的數不能重複的限制),不妨寫作生成函式的形式:

\[f(x) = \prod_^ (1+x^i)

\]再來考慮這個東西的意義……我們按上面的方格圖的形式把它排好,然後第 \(i\) 列從下往上刪去 \(i\) 個方格(可以發現不會發現沒東西刪的情況,因為第 \(i\) 列的格仔數顯然不小於 \(i\)),容易發現上面的行仍然大於等於下面的行,當然這沒有什麼用。現在減去這個之後形式變成了類似於整數拆分的形式,並且有限制選擇的最大的數。

那麼,列舉選擇的集合的大小:

\[f(x) = \sum_^∞ x^} \prod_^ \dfrac

\]不難發現 \([x^]f(x)\) 就是沒有限制的情況下我們要的結果。

但是現在帶限制。首先上界我們不用管,可以考慮對 \(x^n\) 之類的東西取模;主要的是下界 \(j+2\),在我們上面設計的模型下相當於每一列多了 \(j+1\) 個格仔,只跟 \(j+1\) 有關係。

記 \(f_p(x)\) 為下界為 \(p+1\) 時的生成函式,那麼有:

\[\begin

f_p(x)

&= \prod_^∞ (1+x^i) \\

&= \sum_^∞ x^} x^ \prod_^i \dfrac

\end

\]再記生成函式 \(g(x)\) 表示一下需要減去的貢獻:

\[g(x) = \sum_^ f_j x^j f_(x)

\]把 \(g\) 求出來,更新 \(f\) 的後一半即可。

注意到計算 \(f_p(x)\) 的過程中,\(i\) 的上界為 \(o(\sqrt n)\),後面 \(\prod\) 內的內容是完全揹包(感覺可以和一開始的做法產生有機聯絡,可惜我沒有發現),可以做到 \(o(n \sqrt n)\)。

可以採用其他的手段得到更優的做法。

key observation: 正難則反,視角轉換,生成函式。

int f[500005],g[500005];

void solve(int n)

for(int i=n/2+1;i<=n;++i) sub(f[i],g[i]);

}int main()

f[0]=1;

solve(n);

int ans=1;

for(int i=0;i

write(ans);

return 0;

}

L2 022 重排鍊錶

給定乙個單鏈表 l1 l2 ln 1 ln,請編寫程式將鍊錶重新排列為 ln l1 ln 1 l2 例如 給定l為1 2 3 4 5 6,則輸出應該為6 1 5 2 4 3。每個輸入包含1個測試用例。每個測試用例第1行給出第1個結點的位址和結點總個數,即正整數n 105 結點的位址是5位非負整數,n...

L2 022 重排鍊錶

時間限制 500 ms 記憶體限制 65536 kb 長度限制 8000 b 判題程式 standard 作者 陳越給定乙個單鏈表 l1 l2 ln 1 ln,請編寫程式將鍊錶重新排列為 ln l1 ln 1 l2 例如 給定l為1 2 3 4 5 6,則輸出應該為6 1 5 2 4 3。輸入格式 ...

L2 022 重排鍊錶

l2 022 重排鍊錶 25分 給定乙個單鏈表 l 1 l 2 l n 1 l n 請編寫程式將鍊錶重新排列為 l n l 1 l n 1 l 2 例如 給定l為1 2 3 4 5 6,則輸出應該為6 1 5 2 4 3。每個輸入包含1個測試用例。每個測試用例第1行給出第1個結點的位址和結點總個數,...