Miscellaneous
Codeforces 1119F - Niyaz and Small Degrees
對於一個固定的度數限制 \(x\),顯然有 dp:\(f(u,0/1)\) 表示考慮 \(u\) 以及子樹內的點邊,是否刪除了 \(u\) 連向 \(father_u\) 的邊,這時滿足限制的最小刪邊權值和為多少。
假設所有點的度數都大於等於 \(x\),那麼 \(f(u,0)\) 的轉移應該是先假定所有兒子 \(v\) 都是 \(f(v,0)\),之後再選出若干 \(v\) 加上 \(f(v,1)-f(v,0)\),表示刪除這些點,保證刪完之後 \(u\) 是滿足限制的。
對於度數小於等於 \(x\) 的點,可以掛在父親上,和 \(f(v,1)-f(v,0)\) 一起算。這個可以使用平衡樹做到。
但是對於 \(x\in [0,n-1]\) 我們都要計算答案,是否會超時呢?實際上並不會,用的時間應該是 \(O\sum_{x\in[0,n-1]}\sum_{1\le i\le n} [d_i\ge x])=O(\sum d)=O(n)\)。
所以總的時間複雜度為 \(O(n\log_2 n)\)。
Submission #276341229 - Codeforces
[CCC2019] Tourism
發現顯然有如果要讓遊覽到第 \(n\) 個景點的天數最少,那麼第 \(i\) 天遊覽完前 \(p_i\) 個景點,那麼一定有遊覽到第 \(p_i\) 個景區的最少天數為 \(i\),這樣我們將原本的 \(n\) 個景點分成了 \(\lceil\frac{n}{k} \rceil\) 段,每段有且僅有一天可以遊覽到這其中的景點。於是就可以 \(O(n)\) 的轉移了。
QOJ #2568. Mountains
記 \(f(i,j)\) 表示在矩陣 \(A\) 上從 \((1,1)\) 走到 \((i,j)\) 的路徑元素和最大值。那麼發現有 \(f\) 與 \(A\) 構成雙射,那麼需要統計 \(f\) 的個數。顯然 \(f\) 中的所有值都在 \([0,k]\) 中。
而且根據 \(f\) 的轉移可知,\(f(i,j)\le x\) 的 \((i,j)\) 一定是從 \((1,1)\) 開始階梯狀排列的,只需要統計在 \(n\times m\) 的矩陣中插入 \(k\) 條輪廓線的方案數即可。這個可以將每條輪廓線向左下平移,轉化成求 \(k\) 個起點和終點要求一一對應,每個起點各自到各自的終點的路徑之間互不相交的方案數。然後使用 LGV 引理即可。
Submission #519096 - QOJ.ac
QOJ #3082. Ascending Matrix
與上題類似,\(a_{r,c}=v\) 的限制可以使用多項式插值解決。
Submission #519549 - QOJ.ac
「ZJOI2022」樹
\(f(i,x,y)\) 表示考慮到第 \(i\) 個位置時欽定 \([1,i]\) 中有 \(x\) 個屬於 \(T_1\) 的非葉子節點,\([i+1,n]\) 中有 \(y\) 個屬於 \(T_2\) 的非葉子節點,這時的方案數。
從 \(i\) 轉移到 \(i+1\) 時可以考慮欽定 \(i+1\) 的身份:
欽定 \(i+1\) 是 \(T_1\) 的葉子節點: \(f(i+1,x,y-1)\leftarrow f(i,x,y)\times x\times (y - 1)\)。
欽定 \(i+1\) 是 \(T_2\) 的葉子節點:\(f(i+1,x+1,y)\leftarrow f(i,x,y)\times x\times y\)。
初始化:\(f(1,1,i)=i\)。
求答案:\(F_n=\sum_i f(n-1,i,1)\times i\)。
那麼有一個問題是有可能出現 \(\exists i\) 既是 \(T_1\) 的葉子也是 \(T_2\) 的葉子,這種情況會算兩遍,因此額外加入一個容斥轉移 \(f(i+1,x,y)\leftarrow f(i,x,y)\times x\times y\)。
時間複雜度 \(O(n^3)\)。
提交記錄 #2135370 - LibreOJ (loj.ac)
「AHOI2022」山河重整
可以發現一點是,如果要求 \(S\sube \{1,2,\cdots, n\}\) 能夠滿足可以使用子集元素的和表出 \(1,2,\cdots,n\),那麼充要條件是:
證明有很多種,可以簡單歸納,或者更簡單地透過 \(O(n^2)\) 的 dp 推得。
更進一步,如果有 \(S\) 不滿足上述條件,則 \(S\) 第一個不能表出的數應該是最小的 \(i\) 使得 \(i> \sum_{v\in S,v\le i} v\),此時得出 \(S\) 中 \(< i\) 的數一定只能連續表出到 \(i-1\)。
記 \(f(i)\) 表示使用 \(\le i\) 的數,和為 \(i\) 且能表出 \([1,i]\) 中的所有數字的方案個數。那麼應該有 \(answer=2^n-\sum_{i=0}^{n-1} 2^{n-i-1} f(i)\)。
剩下就是求出 \(f(i)\) 了。
注意到 \(f(i)\) 實際上就是要求滿足上述條件的互異分拆數,那麼我們記 \(h(i)\) 表示互異分拆數,那麼一定會有 \(f(i)=h(i)-g(i)\),其中 \(g(i)\) 表示不滿足上述條件的互異分拆數。根據容斥容易得出 \(g(i)=\sum_{j=0}^{i-1} f(j) \cdot t(i-j,j+2)\),其中 \(t(u,v)\) 表示用大於等於 \(v\) 的各個不同的數字拼出 \(u\) 的方案數。容易發現只有 \(j\le \frac{i}{2}\) 的時候 \(t(i-j,j+2)\) 才可能有值,因此可以類似半線上卷積地根據 \(f(0),\cdots ,f(\lfloor\frac{n}{2}\rfloor)\) 直接求出 \(f(\lfloor\frac{n}{2}\rfloor + 1),\cdots,f(n)\) 的值,遞迴下去就可以了。
提一句 \(h\) 的計算是類似於 \(O(n\sqrt n)\) 求分拆數的 dp,而 \(g\) 的計算可以類似 \(h\) 的計算,因為如果 \(t(i-j,j+2)\) 選出了 \(k\) 個數,那麼可以每個數減去 \((j+2)\),總共減去 \((j+2)k\) 之後再求出這 \(k\) 個數的和為 \(i-j-(j+2)k\) 的方案數,我們可以用求 \(h\) 的方法加上精妙的改動後用來求 \(g\),具體可見程式碼。
時間複雜度 \(T(n)=O(n\sqrt n) + T(n/2)=O(n\sqrt n)\)。
提交記錄 #2135608 - LibreOJ (loj.ac)
「2021 集訓隊互測」這是一道集訓隊胡策題
妙妙妙計數題,這兩個月遇到的計數題讓我以為我自己根本不會計數。
考慮如果 \(\sum_{i,j} ([a_i=c_{i,j}]+[b_j=c_{i,j}]-[a_i=b_j])=n^2\) 那麼一定滿足條件。而且顯然有 \(\sum_{i,j} ([a_i=c_{i,j}]+[b_j=c_{i,j}]-[a_i=b_j])\le n^2\)。因此只要讓這個式子儘量去取到最大值即可。發現 \(\sum_{i,j}[a_i=b_j]=(\sum_{i} a_i)(\sum_{j} b_j)+(n-\sum_{i} a_i)(n-\sum_{j} b_j)\),那麼就可以將 \(a\) 和 \(b\) 分離單獨處理。
對於每個 \(x=\sum_{i} a_i\),計算出最大的 \(\sum_{i,j}[a_i=c_{i,j}]\),並算出有多少種方案可以得出這個最大值。\(y=\sum_j b_j\) 同理。
然後列舉 \(x,y\) 計算即可。單獨計算是 \(O(n)\),但列舉 \(x,y\) 的時間複雜度是 \(O(n^2)\),所以總的時間複雜度為 \(O(n^2)\)。
提交記錄 #2135864 - LibreOJ (loj.ac)
「2020-2021 集訓隊作業」Tour
部分分:\(a_i\ge 0\)。
記 \(c_i=a_{p_i}\),原條件相當於 \(\forall i< n:c_ic_{i+1}\le w\)。
將 \(a\) 按從小到大的順序排序,顯然這不會影響答案。同時建立一個佇列 \(q\),執行以下操作直到 \(a\) 被刪空:
- 記 \(x\) 為當前 \(a\) 中最小的數,\(y\) 為 \(a\) 中最大的數。
- 如果 \(xy\le w\),則在 \(a\) 中彈出 \(x\) 並在 \(q\) 的末尾加入 \(x\),並將 \(x\) 標記為好的;如果 \(xy>w\),則在 \(a\) 中彈出 \(y\) 並在 \(q\) 的末尾加入 \(y\),並將 \(y\) 標記為壞的。
然後可以按照佇列內部的順序依次將隊內元素插入 \(c\) 中,因為無論什麼插入順序只要滿足題目要求就可以計數。這時有一個看似顯然實則非常非常強大的性質,就是如果當前隊首元素為 \(x\),當 \(x\) 為好的時,當前隊中的所有元素與 \(x\) 的乘積一定都 \(\le w\);當 \(x\) 為壞的時,當前隊中的所有元素與 \(x\) 乘積一定都 \(>x\)。
那麼我們在插入時可以記錄當前空位數,初始時只有一個空位,此時考慮要插入 \(x\):
- 如果 \(x\) 為好的,那麼將 \(x\) 插入任意一個空位之後都會有:它的左右兩邊在之後都可以插入新的數,而它本身消耗了一個空位數,所以空位數會增加一個。
- 如果 \(x\) 為壞的,那麼將 \(x\) 插入任意一個空位之後都會有:它的左右兩邊在之後都不能插入新的數,而它本身消耗了一個空位數,所以空位數會減少一個。
而插入時任意空位顯然是等價的,因此考慮記錄空位數即可。
具體的,如果隊中第 \(i\) 個元素為好的,則 \(t_i=1\);否則 \(t_i=-1\)。那麼答案就是:
主要用到了兩個關鍵(但是顯然)的性質:
- 任意插入順序對答案沒有影響。
- 任意數字集合 \(S\),總是存在 \(v\) 使得它和 \(S\) 中任意數的乘積都同時 \(\le w\) 或者 \(>w\)。換句話說,對於是否滿足題目的要求,一定存在一個數使得後續的插入均滿足或均不滿足。
部分分:\(n\le 2000\)。
不妨把 \(0\) 當作正數考慮。
顯然一正一負是滿足限制的。
因此可以按正負分段,設正數極長連續段個數為 \(i\),負數極長連續段個數為 \(j\),應該有 \(|i-j|\le 1\),這樣就做到了正負分離,只需要分別計算正數 \(i\) 段的方案數和負數 \(j\) 段的方案數並乘起來即可。
記 \(F(i)\) 表示正數的極長連續段個數為 \(i\) 時的方案數(負數同理)。我們希望求出這個,但是很困難。一個想法是將上個部分分的初始一個空位改成 \(i\) 個。這樣計算出的實際上是至多 \(i\) 個連續段的方案數記為 \(f(i)\),且如果恰有 \(x\) 個極長連續段,則這種方案會在 \(f(i)\) 中被計算 \({i\choose x}\) 次。
那麼就有二項式反演: \(f(i)=\sum_{j\le i} {i\choose j} F(j)\Rightarrow F(i)=\sum_{j\le i} (-1)^{i+j}{i\choose j} f(j)\)。
可以 \(O(n^2)\) 求出 \(F\) 和 \(f\)。然後同理求出負數的 \(G\) 和 \(g\),然後列舉 \(f,g\) 來 \(O(n)\) 合併。
如果想要做到可過的時間複雜度,就需要一些多項式科技了。
首先可以知道 \(f(x) = \prod_{i=1}^n(x+\sum_{j=1}^{i-1}t_j)=\prod_{i=1}^n (x+s_i)\),這個可以分治求出,然後多點求值求出 \(f(0),f(1),\cdots,f(n)\) 的值。
發現:
可以得出 \(\mod x^{n+1}\) 意義下的 \(F(x)\) 的下降冪多項式的係數,因為此時是下降冪多項式,因此多點求值是好做的。
但是到此為止了嗎?
繼續觀察,發現難寫的地方是對於 \(f(x)\) 多點求值,但是如果 \(f(x)\) 是下降冪多項式形式就會很簡單,因此我們直接在分治卷積的時候就把 \((x+s_i)\) 當作下降冪多項式,這樣進行下降冪多項式乘法就可以快速求出 \(f(0),\cdots,f(n)\) 的值了。
當然常數還是很大,所以我對 \(a_i\ge 0\) 的包進行了單獨判斷才過了。如果寫 4-base 的 NTT 會快很多。
提交記錄 #2136325 - LibreOJ (loj.ac)