1. 程式人生 > >一些算法(套路)

一些算法(套路)

直接 zoj gpo 分治 平衡 cnblogs 多點 限制 log

概率/期望DP

  有一些概率/期望DP可以快速地推出這樣的式子:
\[ \begin{align} f_i&=a+bf_i\(1-b)f_i&=a\f_i&=\frac{a}{1-b} \end{align} \]
  BZOJ4872

  XSY2472

分治

  有一些問題求得是只包含/不包含一個點的情況,只需要考慮當前\([l,r]\)\([l,mid]\)\([mid+1,r]\)的影響。

  下面來講一道例題

  \(A(x)\)\(n-1\)次多項式,\(B_i(x)\)為一次多項式,\(\forall i\)\(A(x)\mod B_i(x)\)

  直接做是\(O(n^2)\)

的。

  因為\((A(x)\mod C(x))\mod B_i(x)=A(x)\mod B_i(x)\)\(C(x)\mod B_i(x)=0\)

  設當前已經求出了
\[ D_{l,r}=A(x)\mod(\prod_{i=l}^rB_i(x)) \]
  那麽
\[ \begin{align} D_{l,mid}&=D_{l,r}\mod(\prod_{i=l}^{mid}B_i(x))\D_{mid+1,r}&=D_{l,r}\mod(\prod_{i=mid+1}^{r}B_i(x)) \end{align} \]
  所以我們可以遞歸下去做,直到求出所有的\(D_{i,i}\)

  時間復雜度:
\[ T(n)=2T(\frac{n}{2})+O(nlogn)=O(n{log}^2n) \]
  多點求值

  XSY2469

歐拉phi函數

  就是\(\phi\)函數

  誰都知道這個東西是個積性函數。
\[ \phi(ab)=\phi(a)\phi(b)~~~((a,b)=1) \]
  那如果\((a,b)\neq 1\)呢?

  設\(d=(a,b)\)
\[ \begin{align} \phi(a)&=a(1-\frac{1}{p1_1})(1-\frac{1}{p1_2})\cdots(1-\frac{1}{p1_{n1}})\\phi(b)&=b(1-\frac{1}{p2_1})(1-\frac{1}{p2_2})\cdots(1-\frac{1}{p2_{n2}})\\phi(ab)&=ab(1-\frac{1}{p3_1})(1-\frac{1}{p3_2})\cdots(1-\frac{1}{p3_{n3}})\\phi(d)&=d(1-\frac{1}{p4_1})(1-\frac{1}{p4_2})\cdots(1-\frac{1}{p4_{n4}}) \end{align} \]


  可以發現,對於後面那部分
\[ \begin{align} (1-\frac{1}{p1_1})(1-\frac{1}{p1_2})\cdots(1-\frac{1}{p1_{n1}})\times (1-\frac{1}{p2_1})(1-\frac{1}{p2_2})\cdots(1-\frac{1}{p2_{n2}})\=(1-\frac{1}{p3_1})(1-\frac{1}{p3_2})\cdots(1-\frac{1}{p3_{n3}})\times (1-\frac{1}{p4_1})(1-\frac{1}{p4_2})\cdots(1-\frac{1}{p4_{n4}}) \end{align} \]
  如果\(p\)只在\(a\)\(b\)中出現過,那麽只會在\(ab\)中出現。如果同時在\(a\)\(b\)中出現過,那麽會同時在\(ab\)\(d\)中出現。

  所以有
\[ \phi(ab)=\frac{\phi(a)\phi(b)d}{\phi(d)}~~~(d=(a,b)) \]

逆向思維

情況一

  有時候我們做某個操作很不好做,我們可以先把所有操作做完後在一個個回復。

  例如:給以一個圖,有兩種操作:1.刪邊;2.詢問連通性。

  我們可以先把需要刪的邊刪掉,再一個個加回來,用並查集維護連通性。

情況二

  有時候問你\(\forall A\),滿足要求的\(B\)的和。

  我們可以枚舉所有的\(B\),計算每個\(B\)對每個\(A\)的貢獻。

  AGC005F
 

一類全序問題

  有\(n\)個物品,你要依次選擇這些物品,每個物品有三個屬性\(a_i,b_i,c_i\),當你選擇一個物品後,設當前選擇的物品的\(c\)屬性的和為\(s\),那麽選擇這個物品的收益是\(a_i+b_is\),問你最大收益是多少。

  假設我們已經欽定了一個順序。考慮兩個相鄰的物品(不妨設為前兩個),什麽時候當前順序比交換後更優:
\[ \begin{align} a_1+a_2+b_2c_1&>a_2+a_1+b_1c_2\b_2c_1&>b_1c_2\\frac{c_1}{b_1}&>\frac{c_2}{b_2} \end{align} \]
  這樣我們就得到了一個全序關系。

  那麽能不能擴展到任意兩個物品的情況呢?
\[ \begin{align} a_i+b_ix+a_j+b_j(x+y+c_i)&>a_j+b_jx+a_i+b_i(x+y+c_j)\b_jy+b_jc_i&>b_iy+b_ic_j\\frac{c_i+y}{b_i}&>\frac{c_j+y}{b_j}\\end{align} \]
  好像並不太行。我們需要換一種思路。

  假設我們找到了一種最優解,但並不滿足以上的性質,那麽一定可以交換相鄰兩個物品使得答案最優。所以直接排序貪心可以得到最優解。

  如果題目還有其他限制,你也可以在得到這個順序後DP或者幹其他事情。

莫隊

  總所周知,莫隊的時間復雜度和塊大小有關。

  如果塊大小為\(t\),時間復雜度為\(O(\frac{n^2}{t}+mt)\)

  如果塊大小為\(\sqrt n\),時間復雜度為\(O((n+m)\sqrt n)\)

  如果塊大小為\(\frac{n}{\sqrt{m}}\),時間復雜度為\(O(n\sqrt m+m\log m)\)

  所以有時候可以通過調整塊大小來加速。俗稱調參。

一類單點修改區間求和的問題

  有時候我們要修改一個點,求區間和。
  
|算法|修改|求和|
|:----:|:----:|:----:|
|樹狀數組/線段樹|\(O(log n)\)|\(O(logn)\)|
|分塊1|\(O(1)\)|\(O(\sqrt n)\)|
|分塊2|\(O(\sqrt n)\)|\(O(1)\)|

  樹狀數組/線段樹的做法很經典,這裏就不講了。

  分塊1:每次修改把對應的位置和對應的塊的和\(+1\)

  分塊2:每次修改把對應的位置和對應的塊的和\(+1\),然後求出塊內前綴和、塊內後綴和、塊的前綴和。

  有的人就要問了,分塊做法那麽慢,有什麽用呢?

  用處大著呢!當修改次數與詢問次數不平衡的時候,我們可以做到比樹狀數組更優。

  博主曾經用莫隊+分塊1水過了一道\(n=m={10}^6\)的題。跑的比zjt神犇的線段樹合並還快。

和排列有關的問題

  很多問題讓你求對於每一個排列\(A\),如果\(\cdots\),那麽\(\cdots\)

  我們可以考慮從小到大插入這\(n\)個數,插入第\(i\)個數時考慮這個數的貢獻。

用trie實現全部數\(+1\),查詢全部數的異或和

  我們從低位到高位建一棵trie樹。

  從根開始,交換左右子樹,然後對\(0\)的那棵子樹執行同樣的操作(進位)。

莫比烏斯反演的多組詢問

\[ \begin{align} ans&=\sum_{i=1}^lc^{\gcd(i,b)}\&=\sum_{d|s}c^d\sum_{i=1}^l[\gcd(i,s)=d]\&=\sum_{d|s}c^d\sum_{i=1}^{\frac{l}{d}}[\gcd(i,\frac{s}{d})=1]\&=\sum_{d|s}c^d\sum_{i|\frac{s}{d}}\mu(i)\lfloor\frac{l}{id}\rfloor\\end{align} \]

  看起來沒辦法化簡了

  這時候要枚舉\(j=id\)

\[ \begin{align} ans&=\sum_{j|s}\lfloor\frac{l}{j}\rfloor\sum_{i|j}\mu(i)c^\frac{j}{i}\\end{align} \]

  設\(f(x)=\sum_{i|x}\mu(i)c^\frac{x}{i}\)

  這樣\(f(x)\)就可以預處理出來了。
  

一般情況

\[ \begin{align} ans&=\sum_{i=1}^n\sum_{j=1}^mf(\gcd(i,j))\&=\sum_{i=1}^{\min(n,m)}f(i)\sum_{i|j}\mu(\frac{j}{i})\lfloor\frac{n}{j}\rfloor\lfloor\frac{m}{j}\rfloor\&=\sum_{i=1}^{\min(n,m)}\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor\sum_{j|i}f(j)\mu(\frac{i}{j}) \end{align} \]

  這樣可以預處理後面的\(g(n)=\sum_{i|n}\mu(\frac{n}{i})f(i)\)
  每次枚舉前面詢問。
  時間復雜度:\(O(n+T\sqrt{n})\)  

分治FFT

  分治FFT一般有兩個用途。

求很多個多項式的乘積(普通分治)

  設有\(n\)個多項式,次數之和是\(m\),那麽時間復雜度就是\(O(m\log m\log n)\)。一共有\(\log n\)層,每層是\(O(m\log m)\)的。

求一類數列(CDQ分治)

  數列\(f_n=\sum_{i=0}^{n-1}f_ig_{n-i}\)。對於一個分治區間\([l,r]\),先求出\([l,mid]\)的答案,再計算這部分對右邊\([mid+1,r]\)的貢獻。

  時間復雜度:\(O(n\log^2 n)\)
  

矩陣快速冪+FFT

  DP轉移如下:

\[ f_{i+1,j‘,k+v}+=f_{i,j,k} \]

  \(i\leq n,j\leq l,k\leq m\)
  其中\(v\)只與\(j\)有關,最後求\(k=s\)\(k\mod m=s\)的值的和。
  暴力搞的時間復雜度是\(O(l^3m^3\log n)\)的。
  我們可以把這個東西看成一個多項式。

\[ g_{i,j}=\sum_{k\geq 0}f_{i,j,k}x^k \]
  
  轉移就可以看成乘以一個多項式(單項式)。
  如果求的是\(k\mod m=s\)的值的和,就可以看成循環卷積。
  可以先求值,把每個點值拿去跑一遍矩陣快速冪,再插值回來。
  時間復雜度:\(O(ml^3\log n)+\)點值插值的時間復雜度\(O(m^2)/O(m\log m)\)

多組詢問的矩陣快速冪優化DP

  設矩陣大小為\(m\),次數是\(n\),詢問組數是\(t\),樸素的實現是\(O(tm^3\log n)\)的。
  可以先把轉移矩陣的\(i\)次冪求出來。
  每次詢問只需要拿一個\(1\times m\)的矩陣去乘轉移矩陣就行了。每次乘法是\(O(m^2)\)的。
  時間復雜度:\(O(m^3+tm^2\log n)\)

一些算法(套路)