數位dp一直是個人弱項,惦記很久了,最近補了補,感受還行。ubuntu
解決一個區間中,知足某些條件(與每一位有關)的數的數量(或者帶權的和)。spa
作法:考慮求前綴 \([1,x]\) 的答案。code
若是你是新手,請先考慮一下大概要怎麼作,再繼續看字符串
先把位(不必定是十進制)拆開來,而後是一個形如 "dp到第 \(i\) 位,..." 的 dp,通常能夠用記憶化搜索,一位一位的填,使得它看起來友好一些(我的感受這樣可讀性好)。get
而後要解決 \(\le x\) 的限制。每次按照這樣的規則來填數字:it
這個很好理解。好比說如今欽點下來是 \(123***\),\(x=123456\),那後面顯然不能超過 \(456\)。入門
而若是如今欽點下來是 \(122***\),那它就算是 \(122999\),也不會超過 \(x\)。ast
用一個 lim
標記維護當前是否卡到上界。注意它應該被記在 dp 狀態裏。class
求區間知足:任意相鄰兩位的差都不超過 \(2\) 的數,的數量。擴展
dp 狀態:到第幾位,當前選了什麼(以決定下一個可不能夠選),lim
而後每次 dfs 擴展的時候,判斷一下下一個填的是否合法,再加個記憶化,就好了。
求區間知足:將數當作字符串,沒有任何長度 \(\ge 2\) 的迴文串的數,的數量。
沒有任何長度 \(\ge 2\) 迴文串 \(\rightarrow\) 任意一個字符和它前面一個,兩個都不一樣。
這樣就保證了沒有長度等於 \(2,3\) 的迴文串,而後其他的迴文串都是由這兩種擴展出來的,天然也沒有了。
剩下就很好 dp 了,和上一個差很少。
hdu的題,我第一次學數位dp的時候被老師稱做「畢業題」
你要能把這個題寫出來,你數位dp就差很少了
當時看着老師標程打的,如今簡單複習了一下,發現還挺好想的 而後把它秒了,其實就是一個傻逼縫合怪題
要知足三個條件:
區間求知足條件平方和。
這裏涉及到一個帶權求和。帶權求和狀態要變一下,表示從這位開始截取,的帶權和。
好比說 \(x=123\),填好了 \(11*\),知足條件的數有 \(111\),\(113\),\(114\),\(116\),\(118\)
帶權和爲 \(1^2+3^2+4^2+6^2+8^2=126\)
爲何要作一步截取呢?由於要方便轉移。考慮轉移,至關於,我先肯定好後面若干位,在它們的前面都填上相同的數字(這裏至關於放上了 \(1\))
而後填相同的數字能夠看作是加法 (這裏至關於 \(+10\))
而後平方和,總體加,好作吧:再維護數量和一次方和,設爲 dp[...][0/1/2]
,對應數量,和,平方和
設如今總體加的爲 \(a\),後面一位的 dp[...][0/1/2]
記下來爲 nex[0/1/2]
,如今的是 cur[0/1/2]
,則有:
cur[0]+=nex[0]; cur[1]+=nex[1]+nex[0]*a; cur[2]+=nex[2]+2*nex[1]*a+nex[0]*a*a
(就是拆括號搞一下就行)
對於條件:
求 \(\sum\limits_{i=0}^{n-1} \sum\limits_{j=0}^{m-1} \max(i\oplus j-k,0)\)
\(n,m,k\le 10^{18}\)
後面等價成 \(>k\) 的和,減去 \(>k\) 的數量乘以 \(k\)
拆成二進制,作數位 \(dp\)。記下三個 lim
,表示是否卡在 \(n\) 的上界,\(m\) 的上界,\(k\) 的下界 (由於 \(k\) 那邊是個 \(>\) 的限制)
而後上一題相似的求一下帶權和就好了,要維護一下數量和總和。
因爲數位 dp 的基本模型只有一個 log,因此能夠帶不少別的
題意:求區間能整除數位和的數的數量
好比 \(12\) 就知足條件由於 \(1+2\) 是 \(12\) 的倍數
\(x\le 10^{18}\)
注意到數位和不會超過 \(9\times 18=162\)
先枚舉數位和 \(k\),而後 dp 裏設兩維,一維表示當前數位和,一維表示當前數模 \(k\) 的餘數。最後取答案就是 \(\%k=0\),數位和 \(=k\) 的那個狀態
複雜度是 \((9\times \log n^3)\log n\),很是暴力