DP2與DP優化

講者 : 卡

澄清

是真的不會DP優化才教DP優化的,就跟不會String教String一樣

 

我偷了很多簡報

偷誰的沒有一定標準,只是個人喜好

然後我不敢偷IOIC

如果有原作者希望我拿掉,我會拿掉的

DP2

大綱

  • Digit DP(又稱數位DP)
  • 插頭DP(又稱輪廓線DP)
  • 其它

Digit DP

# #芒國手

辣個男人太強了

來點比較溫和的習題

插頭 DP

我用的狀態可能會和某些題解有些出入,但概念是一樣的

\(n*m\)的網格有幾種被 \(1*2\) 與\(2*1\) 的骨牌完全覆蓋的方式

  • \(min(n,m)\leq 10\)
  • \(max(n,m)\leq 1000\)

粉的是決策完了

藍的是正要決策(放骨牌或已有)

 

黃色稱作輪廓線,上面有插頭

粉的是決策完了

藍的是正要決策(放骨牌或已有)

 

黃色稱作輪廓線,上面有插頭

就是綠色那些

 

對藍色這格

朝下的箭頭是上插頭

朝右的箭頭是左插頭

如果上面有覆蓋骨牌的話

那這個插頭的狀態就是1

 

左圖的輪廓線的狀態就是0,1,0,1,1

狀態間怎麼互相轉移呢?

(正常會一次做完一個橫排,每個橫排都是由左往右做)

Case 1

沒有左箭頭也沒有上箭頭:

 

可以放骨牌上去

Case 1

沒有左箭頭也沒有上箭頭:

 

可以放骨牌上去

放的是橫的話

那(0,0)變成(0,1)

Case 1

沒有左箭頭也沒有上箭頭:

 

可以放骨牌上去

放的是直的話

那(0,0)變成(1,0)

Case 2

沒有左箭頭但有上箭頭:

 

這格被上一橫排的骨牌蓋到

 

Case 2

沒有左箭頭但有上箭頭:

 

這格被上一橫排的骨牌蓋到

放不了骨牌上去

(0,1)變(0,0)

Case 2-2

有左箭頭但沒有上箭頭:

 

這格被同一橫排的骨牌蓋到

 

Case 2-2

有左箭頭但沒有上箭頭:

 

這格被同一橫排的骨牌蓋到

放不了骨牌上去

(1,0)變(0,0)

Case 3

有左箭頭也有上箭頭:

 

 

Case 3

有左箭頭也有上箭頭:

 

假的

一個格子不會被兩個骨牌蓋

這個case根本不存在

那我做完一橫排了

怎麼換到下個橫排?

最後一個根本沒用

不可能會有再往右的骨牌

根本不存在的待轉移格子

最後一個根本沒用

不可能會有再往右的骨牌

 

把他踢掉,並在最前面放個新插頭

最後一個根本沒用

不可能會有再往右的骨牌

 

把他踢掉,並在最前面放個新插頭

新插頭也一定是0

 

1,1,0,1,0變成0,1,1,0,1了

最後只要取做完所有橫排然後輪廓線為0,0,0,0,0的就是答案

 

有\(m*n*2^n\)種狀態,O(1)轉移

總時間複雜度\(O(mn\ 2^n)\)

code

最後只要取做完所有橫排然後輪廓線為0,0,0,0,0的就是答案

 

有\(m*n*2^n\)種狀態,O(1)轉移

總時間複雜度\(O(mn\ 2^n)\)

code

 

用這種輪廓線和插頭的想法,可以做到很多事

像是10*12的棋盤中的曼哈頓路徑數量(CDQ論文原題)之類的

習題

你還可以學...

zscoder's blog

USACO Guide Gold / Advanced 

動態dp

DP優化

大綱

  • 資料結構優化 - 單調隊列、BIT
  • 轉移點單調優化(分治優化)
  • 四邊形優化(1D/1D凸凹)
  • 2D/1D凸
  • 矩陣相關
  • 斜率優化(凸包優化)
  • Slope trick(臻斜率優化)
  • Aliens

轉移點單調

函數性值

xD/yD指的是?

xD指的是子狀態數為\(O(n^x)\)

yD指的是轉移次數是\(O(n^y)\)

 

費式數列,則為1D/0D

區間DP,通常為2D/1D

DP優化的本質

DP優化的本質就是觀察性質,

再利用這些性質去找好的方式(如資料結構、分治)以較快的速度

"維護最佳轉移點"

或是

"從很多轉移點同時轉移並取最佳值"

或是

"利用函數的好性質把不好算的東西挖出來"

DP優化到最後會發現其實跟本重點不在DP

 

DP本來就是種思想

不是種科技

單調隊列優化

# #Caido

多重背包問題

$$有n種物品,每種重w_i,價值v_i,有a_i個$$

你現在有一個容量\(W\)的大背包,問可以拿取的最大價值

多重背包問題

現在假設你考慮完了前\(n-1種\)物品,現在只考慮第\(n\)種物品

$$設dp[i]代表背包用掉i容量的所拿取的最大價值,$$

$$ldp[i]代表背包只看前n-1種物品時用掉i容量所拿取的最大價值$$

$$dp_i =\max_{0\le x \le a[i]}{ldp[i - x * w[n]] + x * v[n]}$$ 

多重背包問題

現在假設你考慮完了前\(n-1種\)物品,現在只考慮第\(n\)種物品

$$設dp[i]代表背包用掉i容量的所拿取的最大價值,$$

$$ldp[i]代表背包只看前n-1種物品時用掉i容量所拿取的最大價值$$

$$dp_i =\max_{0\le x \le a[i]}{ldp[i - x * w[n]] + x * v[n]}$$ 

 

$$i只從和自己同餘w[n]的j(<i)轉移過去!!$$

多重背包問題

$$i只從和自己同餘w[n]的j(<i)轉移過去!!$$

$$對0\le i < w[n]做一次單調隊列優化,每次都只讓O(\lfloor\frac{n}{w}\rfloor)個點互相轉移$$

 

多重背包問題

$$i只從和自己同餘w[n]的j(<i)轉移過去!!$$

$$對0\le i < w[n]做一次單調隊列優化,每次都只讓O(\lfloor\frac{n}{w}\rfloor)個點互相轉移$$

 

$$單次O(\lfloor\frac{W}{w[i]}\rfloor) ,每個物品i用O(w[i])次$$

$$O(n)種物品$$

$$總時間複雜度O(nW)$$

 

RMQ相關優化

題意 

在长度为 n 的数列 a 中,求出长度为 m 的严格上升子序列的个数

答案对 109+7\(10^9+7\) 取模

  • $$1\le m \le n \le 1000$$
  • $$1\le a[i] \le 10^9$$

轉移式與naive solution

 

长度为 \(i\) 的子序列的数量。

$$暴力乱做O(mn^2),TLE$$

加上BIT

$$在算dp[i][j]時可以轉移的k是在j前面且a[k]<a[j]$$

要怎麼知道哪些數\(<a[j]\)?

加上BIT

$$在算dp[i][j]時可以轉移的k是在j前面且a[k]<a[j]$$

要怎麼知道哪些數\(<a[j]\)?

離散化 + BIT!

$$先對a離散化$$

$$然後枚舉i來算dp[i]$$

$$dp[i][j] = bit[i - 1].query(a[j] - 1)$$

$$然後在更新BIT[i]$$

 

總時間複雜度\(O(mn  log  n)\)

 

習題

轉移點單調優化

不論是支語還是英文,都是分治優化

和四邊形優化的關聯

可以先了解一下甚麼是凸單調,什麼是凹單調(看下一頁)

凸單調一定可以用轉移點單調優化做

反向則不然

什麼是轉移點單調

$$cost[i][j]為i轉移到j的代價,以下我們假設cost[i][j]為已知$$

$$求dp[r]=\min_{l<r}a[l][r] = \min_{l<r}(dp[l - 1] + cost[l][r])$$

$$如果dp[i]由dp[k-1]轉移過去最好,那我們稱K[i]=k為i的最佳轉移點$$

$$轉移點單調指的是\forall i < j,K[i]\le K[j]$$

 

然後這樣可以分治做

$$O(n^2)變O(n  log  n)$$

# #syl

CDQ分治套轉移點單調優化

有時轉移來源無法先預處理出來,(\(cost[i][j]\)仰賴\(dp[i-1]\))

這時要用CDQ分治套轉移點單調做

也就是算到[L,R]時先做出[L,mid]的DP值

接著做分治(轉移點單調優化),用[L,mid]來更新[mid,R]

再做出[mid,R]的DP值

 

由主定理證得時間複雜度為\(O(n  log^2 n)\)

CDQ分治套轉移點單調優化

//轉移點單調優化也可以做"題目是要求min,然後cost有凸性值"
...
int dp[N];

int cost (int l, int r) {
    ...
}

void dc (int fromL, int fromR, int L, int R) { //[L, R]由[fromL, fromR]轉移,試試看mid從[fromL,fromR]轉移
	if (L > R) return;
    int mid = (L + R),  opt = INT_MAX, optid = fromL;
    for (int i = fromL; i <= min(fromR, L); i++) {
    	int woo = cost(i, mid);
        if (woo < opt) opt = woo, optid = i;
    }
    dp[mid] = opt;
    if (L == R) continue;
    dc(fromL, optid, L, mid - 1);
    dc(optid, fromR, mid + 1, R);
}

void cdq (int L, int R) {
	if (L == R) return;
    int mid = (L + R) >> 1;
    cdq(L, mid);
    dc(L, mid, mid + 1, R);
    cdq(mid + 1, R);
}

int main () {
    ...
    cdq(1, n);
    ...
}

四邊形優化

適用於\(1D/1D\)求\(min/max\)

凸完全單調性

  • 對於所有轉移點\(i,j(i < j)\)都存在一個\(k\),使的在\(k\)前從\(i\)轉移比較好,\(k\)後從\(j\)轉移比較好。可想成新同學都會優超
  • 求\(dp[r] =min\{a[l][r]\}\)時,$$$$ \(當i < j時,若a[i][x] \ge a[j][x],則對於所有y > x,有a[i][y] \ge a[j][y]\)
  • \(dp[r] =max\{a[l][r]\}\),相當於\(-dp[r]=min(-a[l][r])\),所以變號變成\(a[i][x] \le a[j][x]\)推得\(a[i][y] \le a[j][y]\)
  • 最佳轉移點會遞增
  • 可以用轉移點單調優化做

凹完全單調性

  • 對於所有轉移點\(i,j(i < j)\)都存在一個\(k\),使的在\(k\)前從\(j\)轉移比較好,\(k\)後從\(i\)轉移比較好。
  • 求\(dp[r] =min\{a[l][r]\}\)時,$$$$ \(當i < j時,若a[i][x] \le a[j][x],則對於所有y > x,有a[i][y] \le a[j][y]\)
  • 求\(dp[r] =max\{a[l][r]\}\)時,$$$$ \(當i < j時,若a[i][x] \ge a[j][x],則對於所有y > x,有a[i][y] \ge a[j][y]\)
  • 最佳轉移點會遞減
  • 可能可以用轉移點優化來做(但要反過來寫)(沒人這樣,但應該要可以)

更多說明與例題(#偷 #Caido)

$$註:藍線左端是pre_{l1/2},紅線是pre_i-L_i-K,兩者相減就是我們要的$$

四邊形不等式

  • $$\forall i_1 \le i_2, j_1 \le j_2,f[i_1][j_1]+f[i_2][j_2]\le f[i_1][j_2] + f[i_2][j_1]$$

求\(min時,a\)符合這個不等式便有凸完全單調性。不符合不代表不單調

也可以看\(cost\)符不符合,正常狀況下,\(cost\)符合則\(a\)符合

看是不是凹要變號,如果是求\(max\)也要變號

Monge Codition

  • $$\forall i, j,f[i][j]+f[i+1][j+1]\le f[i][j+1] + f[i+1][j]$$

等價於

不會證時可以\(O(n^2)\)打表\(O(n^2)\)確認!

可以記"交叉比包含好"這個口訣

習題

  • #zck
  • 某些被分在轉移單單調優化的題目

Knuth-Yao Speedup

適用於有\(2D/1D\)凸性質的DP

Knuth-Yao Speedup

適用於有\(2D/1D\)凸性質的DP

和四邊形優化有關,但跟剛才講的是不同東西

題意 

有一個長度為\(n\)的陣列\(a\)。

你每次(共\(n - 1\)次)可以選一個長度\(\ge 2\)的子陣列,

然後把他切成兩個非空子陣列。

花費的代價是原本子陣列的和。

問切成\(n\)個陣列的最小花費。

  • \(1 \le n \le 5000\)

轉移式與naive solution

令\(dp[i][j]代表把[i,j]切成j - i + 1塊的最小代價\)

$$有dp[i][j] = (\sum_{k = i}^{j}a_k) + (\min_{i\le k < j}dp[i][k] + dp[k + 1][j])$$

 

可以枚舉區塊長度,然後枚舉左界,再枚舉轉移點

時間複雜度\(O(n^3)\)

顯(通)然(靈)

感覺上對一個大區間和一個小區間加一相同新東西,大的增加較多代價

$$對i_1<i_2 \le j,有dp[i_1][j+1] - dp[i_1][j] \ge dp[i_2][j+1] - dp[i_2][j]$$

 

$$假設我們已知dp[l][r]切成dp[l][k]+dp[k + 1][r]最好$$

$$我們來看一下[l,r+1]從k'(<k)轉移和從k轉移哪個比較好$$

$$1.k是[l,r]最佳轉移點:dp[l][k']+dp[k'+1][r]\ge dp[l][k] + dp[k+1][r]$$

$$2.最上面那條:dp[k'+1][r+1]-dp[k'+1][r] \ge dp[k+1][r+1]-dp[k+1][r]$$

$$於是有dp[l][k']+dp[k'+1][r+1]\ge dp[l][k]+dp[k+1][r+1]$$

$$進而得到[l,r+1]的最佳轉移點不比[l,r]的最佳轉移點k小$$

從顯(通)然(靈)得到的性質

$$令K[i][j]是[i,j]的最佳轉移點,便是說[i,j]切成[i,K[i][j]]和[K[i][j]+1,j]最好$$

$$從上一頁結論與其對稱性知K[i][j]\le K[i][j+1]和K[i-1][j]\le K[i][j]$$

$$進而推得K[i][j-1] \le K[i][j]\le K[i+1][j]$$

最後得到

$$K[i-2][j-1]\le K[i-1][j-1] \le K[i][j-1] \le K[i][j]\le K[i+1][j] \le K[i+1][j+1]$$

從顯(通)然(靈)得到的性質

$$令K[i][j]是[i,j]的最佳轉移點,便是說[i,j]切成[i,K[i][j]]和[K[i][j]+1,j]最好$$

$$從上一頁結論與其對稱性知K[i][j]\le K[i][j+1]和K[i-1][j]\le K[i][j]$$

$$進而推得K[i][j-1] \le K[i][j]\le K[i+1][j]$$

最後得到

$$K[i-2][j-1]\le K[i-1][j-1] \le K[i][j-1] \le K[i][j]\le K[i+1][j] \le K[i+1][j+1]$$

 

ㄟˊ 對於相同的\(j-i\),\(K[i][j]\)單調!

作法

一樣先枚舉長度(\(j-i\)),再枚舉左界\(i\)(也得到\(j=i+長度-1\))

$$接著枚舉K[i+1][j]\le k\le K[i][j-1],$$

$$試著轉移並找到[i,j]最好的轉移點,記作K[i][j]$$

 

因為單調,對於同個\(j-i\),最多只要看過\(n\)個轉移點

$$又只有n種j-i$$

所以時間複雜度是\(O(n^2)\),比原本少一個\(n\)

這樣的技巧就叫作Knuth-Yao Speedup(也叫Knuth's Optimization)

 

作法

一樣先枚舉長度(\(j-i\)),再枚舉左界\(i\)(也得到\(j=i+長度-1\))

$$接著枚舉K[i+1][j]\le k\le K[i][j-1],$$

$$試著轉移並找到[i,j]最好的轉移點,記作K[i][j]$$

 

因為單調,對於同個\(j-i\),最多只要看過\(n\)個轉移點

$$又只有n種j-i$$

所以時間複雜度是\(O(n^2)\),比原本少一個\(n\)

這樣的技巧就叫作Knuth-Yao Speedup(也叫Knuth's Optimization)

可以看dp符不符合Monge Condition(交叉比包含小)來判斷凸性

4頁前的那個靠感覺得出的式子,移項後其實也符合Monge Condition

dp

參考code

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long

const int N = 5010;
int n, a[N];
ll pre[N], dp[N][N], opt[N][N]; //opt存最佳轉移點


int main () {
    cin >> n;
    pre[0] = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        pre[i] = a[i] + pre[i - 1];
        opt[i][i] = i;
        dp[i][i] = 0;
    }
    
    for (int len = 2; len <= n; len++) {
        for (int i = 1, j = len; j <= n; i++, j++) {
            dp[i][j] = LLONG_MAX;
            for (int k = opt[i][j - 1]; k <= opt[i + 1][j]; k++) {
                if (dp[i][k] + dp[k + 1][j] < dp[i][j]) {
                    dp[i][j] = dp[i][k] + dp[k + 1][j];
                    opt[i][j] = k;
                }
            }
            dp[i][j] += pre[j] - pre[i - 1];
        }
    }
    
    /*
    //驗算有沒有monge condition,可以自己載cses測資試試看
    bool y = 1;
    for (int i = 1; i <= n; i++) for (int j = i; j + 1<= n; j++) {
        if (dp[i][j] + dp[i+1][j+1] > dp[i][j+1] + dp[i+1][j] ) y = 0;
    }
    if (y == 0) cout << "No monge condion!";
    */
    
    cout << dp[1][n] << '\n';
return 0;}

不一定只能用在區間DP喔

題意 

$$你要在一條有n個房子(a[1]\le a[2]\le... \le a[n])的大街上蓋m間學校$$

$$這n間房子的都裡都住著一個人$$

每個人都會走去離自己最近的郵局

你想要讓大家走的總距離最小

小觀察1

先想想看只蓋一棟郵局時答案是甚麼?

答案就是把郵局蓋在房子位置的中位數

 

小觀察1的口糊證明

$$我們假設n = 2 mid + 1$$

$$如果我今天不把郵局蓋在a[mid],而是蓋在a[mid - 1]呢?$$

$$那前mid - 1的人可以少走a[mid]-a[mid - 1],$$

$$但第mid到第n個人都要多走a[mid] - a[mid - 1]$$

$$總距離比原本多a[mid] - a[mid - 1],還是放在mid比較好$$

\(n\)是偶數的話放在兩個中位數之間的任何一處皆好,此略

 

naive solution

$$dp[i][r]代表在[1, r]蓋i棟房子時,[1,r]的人走的最小總距離$$

 

$$先隨便處理好cost[i][j]代表[i,j]的人都往其中位數跑的總距離$$

$$則dp[1][i] = cost[1][i]$$

$$然後有轉移式dp[i][r] = \min_{k<r}(dp[i-1][k] + cost[k + 1][r])$$

 

$$可以枚舉i(2到m),然後枚舉右界r,再枚舉轉移點k$$

時間複雜度 \(O(mn^2) \le O(n^3)\) ,TLE

回到中位數

要求\(O(n^3)\)的話,\(cost\)可以隨便求

要證這題的性質就需要知道\(cost\)怎麼\(O(n^2)\)

 

$$結論就是 cost[l][r] = cost[l][r-1] + a[r] - a[\lfloor\frac{l + r}{2}\rfloor],然後cost[i][i]=0$$

每次多加一個東西進來,如果目前有奇數個東西,那中位數變成兩個;

[l,r-1]繼續走到較小那個中位數,r走到較大那個中位數。

如果目前有偶數的東西,那我會拔掉較小的那個中位數;

原本走到較小那個中位數的會改去較大那個中位數,可以證明\(cost\)同,

然後再加上\(r\)走到新中位數(原本較大得那個)的距離。

回到中位數

$$結論就是 cost[l][r] = cost[l][r-1] + a[r] - a[\lfloor\frac{l + r}{2}\rfloor],然後cost[i][i]=0$$

可能會發現\(cost\)符合不等式,我們這裡假設不符合來反證看看

 

$$cost[i][j] + cost[i+1][j+1] > cost[i+1][j] + cost[i][j+1]$$

$$左邊展開:cost[i][j]+(cost[i+1][j]+a[j+1]-a[\lfloor\frac{i+j+2}{2}\rfloor])$$

$$右邊展開:cost[i+1][j]+(cost[i][j]+a[j+1]-a[\lfloor\frac{i+j+1}{2}\rfloor])$$

$$消一消變證a[\lfloor\frac{i+j+2}{2}\rfloor]<a[\lfloor\frac{i+j+1}{2}\rfloor],顯然是錯的,矛盾。$$

回到中位數

$$結論就是 cost[l][r] = cost[l][r-1] + a[r] - a[\lfloor\frac{l + r}{2}\rfloor],然後cost[i][i]=0$$

可能會發現\(cost\)符合不等式,我們這裡假設不符合來反證看看

 

 

所以\(cost\)符合Monge Condition,

進而得到\(dp[i][r]=\min_{k<r}(dp[i-1][k]+cost[k+1][r])\)min裡那串也符合

$$最後得到\forall i,dp[i]有凸單調性$$

好ㄟ

$$所以現在\forall i,dp[i]都有凸單調性$$

可以做\(m\)次1D/1D凸優化或分治優化,時間複雜度是\(O(mn  log  n)\)

更好ㄟ

$$所以現在\forall i,dp[i]都有凸單調性$$

可以做\(m\)次1D/1D凸優化或分治優化,時間複雜度是\(O(mn  log  n)\)

 

$$1.剛剛我們推出來K[i][j] \le K[i][j+1]$$

$$2.顯然地,蓋越多郵局越好,所以有K[i-1][j] \le K[i][j]$$

$$這樣就有K[i][j-1] \le K[i][j]\le K[i+1][j]$$

可以用Kuth-Yao Speedup!\(O(n^2)\)做完!

 

code

然後這題其實還有困難版,要用到Aliens

TIOJ 1986 (Caido's Blog)

比一比

1D/1D凸時間複雜度比較

  • 暴力 : \(O(n^2)\)
  • 轉移點優化 : \(O(n  log  n)\) or \(O(n  log^2  n)\) 
  • 1D/1D凸 : \(O(n  log  n)\)

2D/1D凸時間複雜度比較

  • 暴力 : \(O(n^3)\)
  • 轉移點優化 : \(O(n^2 log  n)\) or \(O(n^2 log^2 n)\)
  • 1D/1D凸優化 : \(O(n^2 log  n)\)
  • Knuth-Yao Speedup: \(O(n^2)\)

轉移點優化、1D/1D凸優化指的是做\(n\)次,

2D/1D凹沒有Knuth's Optimization可用,只能1D/1D凹優化做\(n\)次

但我不知道為什麼QQ

矩陣相關

矩陣快速冪

你應該已經會了

但我還是幫大家(還有笨我)複習一下 (偷自)

費式數列

$$f(n) = f(n - 1) + f(n - 2)$$

費式數列

$$f(n) = f(n - 1) + f(n - 2)$$

moon.h

\(\begin{vmatrix}f(n)&f(n - 1)\end{vmatrix}\) = \(\begin{vmatrix}f(n - 1)&f(n - 2)\end{vmatrix}\) \(\begin{vmatrix}1 &1\\1 &0\end{vmatrix}\)

 

\(\begin{vmatrix}f(n)&f(n - 1)\end{vmatrix}\) = \(\begin{vmatrix}f(1)&f(0)\end{vmatrix}\) \(\begin{vmatrix}1 &1\\1 &0\end{vmatrix}^{n-1}\)

加上常數項\(k\)

$$f(n) = f(n - 1) + f(n - 2) + k$$

加上常數項\(k\)

$$f(n) = f(n - 1) + f(n - 2) + k$$

\(\begin{vmatrix}f(n)&f(n - 1)&k\end{vmatrix}\) = \(\begin{vmatrix}f(n - 1)&f(n - 2)&k\end{vmatrix}\) \(\begin{vmatrix}1 &1&0\\1 &0&0\\1&0&1\end{vmatrix}\)

加上\(n\)

$$f(n) = f(n - 1) + f(n - 2) + n$$

 

加上\(n\)

$$f(n) = f(n - 1) + f(n - 2) + n$$

 

\((n - 1) + 1 = n\)好好維護喔

加上\(n\)

$$f(n) = f(n - 1) + f(n - 2) + n$$

 

\((n - 1) + 1 = n\)好好維護喔

要記得維護\(1\)喔

加上\(n\)

$$f(n) = f(n - 1) + f(n - 2) + n$$

 

\((n - 1) + 1 = n\)好好維護喔

要記得維護\(1\)喔

\(\begin{vmatrix}f(n)&f(n - 1)&n&1\end{vmatrix}\) = \(\begin{vmatrix}f(n - 1)&f(n - 2)&n-1&1\end{vmatrix}\) \(\begin{vmatrix}1 &1&0&0\\1&0&0&0\\1&0&1&0\\1&0&1&1\end{vmatrix}\)

求和

$$f(n) = f(n - 1) + f(n - 2)$$

$$S(n) = \sum_{1\leq i\leq n} f(i)$$

求和

$$f(n) = f(n - 1) + f(n - 2)$$

$$S(n) = \sum_{1\leq i\leq n} f(i)$$

 

\(\begin{vmatrix}f(n)&f(n - 1)&S(n-1)\end{vmatrix}\) = \(\begin{vmatrix}f(n - 1)&f(n - 2)&S(n-2)\end{vmatrix}\) \(\begin{vmatrix}1 &1&1\\1&0&0\\0&0&1\end{vmatrix}\)

矩陣快速冪
經典題&小技巧

有張簡單有向圖有\(n\)點

給你一個數字\(k\)

問從\(1\)號點走到\(n\)號點花恰好\(k\)步有幾種走法?(要取模)

  • \(n\leq 100\)
  • \(k\leq 10^9\)

\(k\)很小怎麼做?

\(k\)很小怎麼做?

\(dp[i][u]\)代表走了\(i\)步停在\(u\)的走法

則如果\(u\)有條邊連向\(v\),\(dp[i][u]\)可轉移至\(dp[i+1][v]\)

 

\(dp[k][n]\)即為所求

\(k\)很大呢?

 

\(k\)很大呢?

你已經知道了

我們可以把轉移式用矩陣寫出來(我討厭latex)

\(\begin{vmatrix}dp_{i,1} & dp_{i,2} &\cdots& dp_{i,n}\end{vmatrix}\) = \(\begin{vmatrix}dp_{i-1,1} & dp_{i-1,2} &\cdots& dp_{i-1,n}\end{vmatrix}\) \(\begin{vmatrix}w_{1,1} & w_{1,2} &\cdots& w_{1,n}\\w_{2,1}&w_{2,2}&\cdots&w_{2,n}\\\vdots&\ddots&&\vdots\\\vdots&&\ddots&\vdots\\w_{n,1}&w_{n,2}&\cdots&w_{n,n}\end{vmatrix}\)

其中\(w_{i,j}\)代表\(i\)有無連一條邊到\(j\)

\(k\)很大呢?

所以就可以矩陣快速冪了,時間複雜度\(O(n^3 log\ k)\)

\(\begin{vmatrix}dp_{k,1} & dp_{k,2} &\cdots& dp_{k,n}\end{vmatrix}\) = \(\begin{vmatrix}1 & 0 &\cdots& 0\end{vmatrix}\) \(\begin{vmatrix}w_{1,1} & w_{1,2} &\cdots& w_{1,n}\\w_{2,1}&w_{2,2}&\cdots&w_{2,n}\\\vdots&\ddots&&\vdots\\\vdots&&\ddots&\vdots\\w_{n,1}&w_{n,2}&\cdots&w_{n,n}\end{vmatrix}^k\)

也可以做最短路

有結合律的操作(\(a(bc)=(ab)c\))都可以快速冪(不限矩陣)喔

 

 

$$有一個n*n矩陣A和一整數k$$

$$求A^1 + A^2+...+A^k (要取模)$$

  • \(n\leq 40\)
  • \(k \leq 10^9\)

分治!!!!!!

$$注意到\sum_{1\leq i \leq k}A_i = (1+A_{mid})\sum_{1\leq i \leq mid}A_i$$ 

可以先算出\(A_mid\),再切成一半,遞迴左半邊,右半邊就由左半邊得到

當然\(k\)是奇數時會稍微不一樣

分治!!!!!!

$$注意到\sum_{1\leq i \leq k}A_i = (1+A_{mid})\sum_{1\leq i \leq mid}A_i$$ 

可以先算出\(A_mid\),再切成一半,遞迴左半邊,右半邊就由左半邊得到

當然\(k\)是奇數時會稍微不一樣

 

總共遞迴\(log\ k\)次,每次\(O(n^3 log\ k)\)

總時間複雜度\(O(n^3 log^2k)\)

現在有一個變形的費式數列

$$f(i) = f(i - k) + f(i - k + 1) + ... + f(i - 1)$$

求\(Q\)次\(f(n)\)

  • \(k \leq 16\)
  • \(n \leq 10^{18}\)
  • \(Q \leq 10 ^ 5\)

我會,矩陣快速冪!

\(A=\begin{vmatrix}1&1&0&\cdots&0\\1&0&1&\cdots&0\\1&0&0&\cdots&0\\\vdots&&\ddots&&\vdots\\1&0&0&\cdots&1\\1&0&0&\cdots&0\end{vmatrix}\)

\(\begin{vmatrix}f(n)&f(n-1)&\cdots&f(n-k+1)\end{vmatrix}\) \(\ =\ \begin{vmatrix}f(n-1)&f(n-2)&\cdots&f(n-k)\end{vmatrix}\ A\)

\(\begin{vmatrix}f(n)&f(n-1)&\cdots&f(n-k+1)\end{vmatrix}\)  \(\ =\ \begin{vmatrix}f(k-1)&f(k-2)&\cdots&f(0)\end{vmatrix}\ A ^{n- k +1}\)

 

$$中間算A的冪次時是k*k矩陣互乘,$$

$$時間複雜度O(Q k^3 log \ C)$$

我會,矩陣快速冪!

$$O(Q k^3 log \ C)$$

 

TLE了...

(乘起來高達\(2.5*10^{10}\))

考慮預處理

可以先做出\(A^1,A^2,A^4,...\)共\(log\ C\)個矩陣

$$A^{2^x}可直接仰賴A^{2^{x-1}},所以只要一個log$$

對於單一\(k\)的時間複雜度為\(O(k^3 log\ C)\)

\(k\)會變的話就是\(O(k^4 log\ C)\)

考慮預處理

對於一個詢問我就只要讓

 \(\begin{vmatrix}f(k-1)&f(k-2)&\cdots&f(0)\end{vmatrix}\)

一直乘\(A\)的冪次就好

最多乘\(log C\)次,每次都是\(1*k\)和\(k*k\)的矩陣相乘

單次詢問時間複雜度\(O(k^2 log\ C)\)

 

整題的總時間複雜度為\(O((k^2 log\ C)(Q+k^2))\)

 

我沒(不)有(想)要(要)講(學)剩(詭)下(譎)的

Caidol簡報

習題

斜率優化

#  #caido

Slope trick

維護線段和轉折點,是真正的斜率優化

Slope-trick-able

當一個連續的凸/凹函數,可以被切成很多段直線時

我們稱這個函數Slope-trick-able

Slope-trick-able

當一個連續的凸/凹函數,可以被切成很多段直線時

我們稱這個函數Slope-trick-able

左邊的凸函數就是一個例子

 

Slope-trick-able

當一個連續的凸/凹函數,可以被切成很多段直線時

我們稱這個函數Slope-trick-able

左邊的凸函數就是一個例子

可以用斜率、轉折點存所有資訊

Slope-trick-able

當一個連續的凸/凹函數,可以被切成很多段直線時

我們稱這個函數Slope-trick-able

左邊的凸函數就是一個例子

可以用斜率、轉折點存所有資訊

上面這個和左圖無關

Slope-trick-able

當一個連續的凸/凹函數,可以被切成很多段直線時

我們稱這個函數Slope-trick-able

左邊的凸函數就是一個例子

可以用斜率、轉折點存所有資訊

常常會用multiset之類的來存

如果斜率一次變不只1

那就加同個點很多次

Slope-trick-able

兩個凹凸性同的slope-trick-able的函數相加依然slope-trick-able

 

然後新函數的斜率分割點是兩者的分割點取交集後,把一些丟掉

+1和-1抵銷之類的

你有一個長度為\(n\)的數列\(a\)

你每次操作可以使隨便一個數\(+1或-1\)

問最少的操作次數使得\(x\)非嚴格遞增

  • \(n \leq 2*10^5\)
  • \(a_i \leq 10^9\)

\(O(nC)\)DP解

令\(dp[i][x]\)代表使得\(a_i\)\(\leq\)x時的答案

有轉移式

$$dp[i][j]=\min(dp[i][x-1],\ \ |x-a_i|+\min_{1\leq k \leq x}dp[i-1][k])$$

\(dp[n][C]即為所求\)

\(O(nC)\)DP解

令\(dp[i][x]\)代表使得\(a_i\)\(\leq\)x時的答案

有轉移式

$$dp[i][j]=\min(dp[i][x-1],\ \ |x-a_i|+\min_{1\leq k \leq x}dp[i-1][k])$$

\(dp[n][C]即為所求\)

 

穩TLE

小觀(通)察(靈)

一定存在一個最佳解,

使得最後的陣列裡的所有數字都是原陣列裡出現過的數字

 

小觀(通)察(靈)

一定存在一個最佳解,

使得最後的陣列裡的所有數字都是原陣列裡出現過的數字

小觀察的證明

留給讀者作為練習

 

\(O(nC)\)DP解

令\(dp[i][x]\)代表使得\(a_i\)\(\leq\)x時的答案

有轉移式

$$dp[i][j]=\min(dp[i][x-1],\ \ |x-a_i|+\min_{1\leq k \leq x}dp[i-1][k])$$

 

套用小觀察就會知道有用的\(x\)只有\(n\)個

所以時間複雜度就變\(O(n^2)\)

會過CF  713C

\(O(nC)\)DP解

令\(dp[i][x]\)代表使得\(a_i\)\(\leq\)x時的答案

有轉移式

$$dp[i][j]=\min(dp[i][x-1],\ \ |x-a_i|+\min_{1\leq k \leq x}dp[i-1][k])$$

 

套用小觀察就會知道有用的\(x\)只有\(n\)個

所以時間複雜度就變\(O(n^2)\)

會過CF  713C

更好的做法?

$$dp[i][j]=\min(dp[i][x-1],\ \ |x-a_i|+\min_{1\leq k \leq x}dp[i-1][k])$$

 

把\(dp_i,dp_i-1\)當成函數來想

每次算\(dp_i(x)\)的時候就相當於先讓\(dp_{i-1}(x)\)加上\(|x-a_i|\)

再前綴取\(min\)

加上\(|x-a_1|\)

前綴取min

得到\(dp_1\)

\(加上|x-a_2|\)

\(加上|x-a_2|\)

前綴取min

得到\(dp_2\)

\(加上|x-a_3|\)

\(加完了|x-a_3|\)

前綴取min

得到\(dp_3\)

疑?

\(dp\)好像一直都是slope trick able,而\(|x-a_i|\)本來就是slope trick able

疑?

\(dp\)好像一直都是slope trick able,而\(|x-a_i|\)本來就是slope trick able

每次\(i\)變大,就是讓\(a_i\)左邊的線斜率減一,讓其右邊的線斜率加一

 

疑?

\(dp\)好像一直都是slope trick able,而\(|x-a_i|\)本來就是slope trick able

每次\(i\)變大,就是讓\(a_i\)左邊的線斜率減一,讓其右邊的線斜率加一

把原本最右的轉折點拔掉(如果\(a_i\)較大那就不拔)

同時在\(a_i\)這個位置,加兩個轉折點

疑?

\(dp\)好像一直都是slope trick able,而\(|x-a_i|\)本來就是slope trick able

每次\(i\)變大,就是讓\(a_i\)左邊的線斜率減一,讓其右邊的線斜率加一

把原本最右的轉折點拔掉(如果\(a_i\)較大那就不拔)

同時在\(a_i\)這個位置,加兩個轉折點

 

前綴取min就是把斜率剛從0變成1的整段拔掉

函數的最右邊還是保持水平線

作法出來了喔

我整個函數一直都是這種可以由轉折點、斜率紀錄的函數

又我們最後想要的答案\(dp_n(C)\)是\(dp_n\)中的水平線的\(y\)座標

也就是說我們只需要維護最右邊的轉折點及其y座標

作法出來了喔

我整個函數一直都是這種可以由轉折點、斜率紀錄的函數

又我們最後想要的答案\(dp_n(C)\)是\(dp_n\)中的水平線的\(y\)座標

也就是說我們只需要維護最右邊的轉折點及其y座標

 

可以開個priority_queue存轉移點,同時記錄答案

作法出來了喔

我整個函數一直都是這種可以由轉折點、斜率紀錄的函數

又我們最後想要的答案\(dp_n(C)\)是\(dp_n\)中的水平線的\(y\)座標

也就是說我們只需要維護最右邊的轉折點及其y座標

 

可以開個priority_queue存轉移點,同時記錄答案

每次加一個\(a_i\)進來,就先放兩個\(a_i\)進入pq,再把pq中最大值踢掉

踢掉時順便看一下答案的變化

 

時間複雜度 \(O(n log n)\)

習題(自caido簡報)

Aliens

變數要叫ufo

#  #caido

謝謝大家

好想進二階

然後揪資讀\(\frac{1}{3}\)馬拉松

                                                       (從建中跑到大湖公園站)

DP2與DP優化

By weakweakweak

DP2與DP優化

  • 158