高手訓練 數字DP系列(暫完)

2022-02-07 13:18:28 字數 3832 閱讀 5656

也就是不停講題寫題,沒講什麼實質性數字\(dp\),甚至只有後面三題有點點味道。

前面就是用到了數字的相關知識而已。

題目大意

多組資料,每組幾個操作\(opt\)與n個數,\(opt\)可能是\(and、xor、or\),求任意兩數\(opt\)運算後的最大值。

solution

顯然我們得分操作進行。

\(xor\)

當\(opt=xor\)時,你會發現這題十分熟悉。

the xor largest pair(隨手網上找的oj)

我們發現對於乙個0/1,我們需要找到和它相對的方向走

比如當一位為1,我們要有最大代價,就應該找0與之形成貢獻。

然而對於找不到相對值的位置,沒有辦法,只能找相同數了

直接乙個\(trie\)樹板子套上去就行了。

\(\mathrm\)

struct trie

}inline int ask(int x)

return ans;

}inline void clear()

} tr;

\(and\)

(直接念題解:)

從高位到低位貪心。畢竟and的限制性還是比較強的。

\(\mathrm\)

int vis[n] = {};

for(int i = 30; i >= 0; --i)

if(op <= 2)break;

if(cnt > 1)

}int ans = (1 << 30) - 1;

for(int i = 1; i <= n; ++i)

if(!vis[i])ans &= a[i];

\(or\)

or比較煩,因為限制少,答案難求,但我們依然從高位到低位貪心。

考慮每次都盡量取優。列舉某個數的不同位。

我們對於每個數,找到滿足最優條件(如上規則)的情況下,最小的乙個值,記為\(val\)。可以理解為:當某一位可選可不選的時候,讓它為0.

這麼搞出來的\(val\)一定是某個可行解的子集。

最後拼出的答案即為所求。

狀壓\(dp\)

\(\mathrm\)

memset(f, 0, sizeof(f));

for(int i = 1; i <= n; ++i)f[a[i]] = 1;

for(int i = (1 << 20) - 1; i >= 0; --i)

for(int j = 0; j <= 20; ++j)

f[i] |= f[i | (1 << j)];

code

完整**:

#include#define n 200010

#define int long long

int n, a[n] = {};

int k;

inline int read()

void write(int x)

//io

int f[1 << 21] = {};

struct trie

}inline int ask(int x)

return ans;

}inline void clear()

} tr;

//trie樹

void work()

; for(int i = 30; i >= 0; --i)

if(op <= 2)break;

if(cnt > 1)

}int ans = (1 << 30) - 1;

for(int i = 1; i <= n; ++i)

if(!vis[i])ans &= a[i];

write(ans);

putchar(10);

return ;

}if(k == 2)

if(k == 3)

write(sum);

putchar(10);

}}main()

題目大意

給定整數\(m,k\),求出正整數\(n\)使得\(n+1,n+2,...,2n\)中恰好有個\(m\)數在二進位制下恰好有\(k\)個\(1\)。有多組資料。

solution

這種題一上來就沒頭沒腦的,該怎麼做?

稍微手推幾組相鄰答案,嘗試尋找關聯。

感性理解:

而我們通過字首和的方式求解\(num(n)\)。

求\(s(x)\)

對於答案

\(\mathrm\)

#include #define int long long

using namespace std;

int n, m;

int read()

void write(int x)

int c[85][85];

inline int ask(int x)

return ans;

}inline bool check(int x)

int find()

return ans;

}void work(void)

if (n == 1 && m == 1)

int k1 = find();

++n;

int k = find();

write(k1);

putchar(32);

write(k - k1);

putchar(10);

return void();

}void pre(void)

main()

題目大意

乙個數字被稱為好數字需滿足下列條件:

①它有個\(2 \times n\)數字,\(n\)是正整數(允許有前導\(0\))。

②構成它的每個數字都在給定的數字集合\(s\)中。

③它前\(n\)位之和與後\(n\)位之和相等或者它奇數字之和與偶數字之和相等

例如,對於\(n=2\),\(s=\\),合法的好數字有\(8\)個:

\(1111\),\(1122\),\(1212\),\(1221\),\(2112\),\(2121\),\(2211\),\(2222\)。

已知,求合法的好數字個數\(mod\ 999983\)。

solution

我們可以通過一些操作得出一些奇怪結論。

然後就tm瞎算。

\(\mathrm\)

#include #define n 1010

#define mod 999983

#define int long long

using namespace std;

int n, m;

inline int add(int a, int b)

inline int del(int a, int b)

int read()

void write(int x)

int a[11] = {}, cnt = 0;

int f[n][n * 10] = {};

void work()

main()

後面的題都沒寫了

雖然真正的數字\(dp\)都在後面,但是我寫不動了。

鴿了吧。

學習筆記 訓練記錄 數字DP

數字dp,即對數字進行拆分,利用數字來轉移的一種dp,一般採用記憶化搜尋,或者是先預處理再進行轉移 乙個比較大略的思想就是可以對於給定的大數,進行按數字進行固定來轉移記錄答案 區間型別的,可以考慮字首和的思想,求 l,r 可以看做求 1,r 1,l 其實還有一種,是按照二進位制建一顆0,1樹來表示,...

HDU訓練記錄2 基礎數字dp

題目描述 傳送門題意 求0 n中含 49 的數的個數。題解狀態 f i j 表示i位數所有以j開頭的數中合法 不含 49 的數的個數。轉移 if j 4 k 9 f i j f i 1 k 列舉jk分別為i和i 1位數的開頭並且滿足條件。求解時用總數減去dp值。注意 這道題傳m 1的話有可能爆lon...

動態規劃學習系列 數字DP(初識)

第一次知道數字dp這東西,是在大二新手賽,那時有一道 cutting trees 的題目,現在來看就是水題一道,可以用多種方法水過,可惜當時愣是沒做出來,其他水題也沒做出來,於是被大一虐成翔。抱著學習的態度,我們再來看看這道題。多組詢問,每組詢問a和b,為 a,b 範圍內,有多少個數是由乙個上公升序...