HNOI2012 集合選數 狀壓 dp

2022-04-28 19:51:08 字數 1571 閱讀 4515

求對於正整數\(n\leq 1e5\),的滿足約束條件:"若\(x\)在該子集中,則\(2x\)和\(3x\)不在該子集中."的子集個數.

是一道很妙的構造+狀壓\(dp\)題吖.

我最開始想這題的時候畫了乙個如下的圖.對於每乙個點,左兒子是它的兩倍,右兒子是它的三倍.

約束條件是:連了邊的兩個點是不可以同時選的,也就是只能隔乙個選乙個,但是這樣顯然不好做.於是考慮能不能再轉化一下.仔細觀察這個圖會發現它特別像一棵樹,但又不是,因為乙個點有兩個父親,這是因為乙個數可能是乙個數的兩倍同時又是另外乙個數的三倍.再觀察一下會發現這個圖似乎是由許多小菱形組成的,於是把菱形拉成正方形會發現得到了乙個倒三角.如下:

顯然我們可以把這個倒三角填滿得到乙個網格圖.對於每乙個點,它的下面是它的兩倍,右邊是它的三倍.這樣一來,約束條件就變成了選了乙個數,就不能選與它相鄰的數(上,下,左,右).轉化之後就成為了一般的狀壓$dp $解決的問題.但是,注意到這個**並不能涵蓋所有的數,我們需要對沒有被涵蓋的數再建乙個如上的網格圖,最後乘法原理統計下答案就好了.

溫馨提醒​

大陣列別用\(memset\),你很有可能會向我一樣\(t\)掉.

code

```cpp

#include#define il inline

#define ri register int

#define go(i,a,b) for(ri i=a;i<=b;++i)

#define yes(i,a,b) for(ri i=a;i>=b;--i)

#define e(i,u) for(ri i=b[u];i;i=a[i].nt)

#define mem(a,b) memset(a,b,sizeof(a))

#define ll long long

#define db double

#define inf 2147483647

using namespace std;

il int read()

while(c>='0'&&c<='9')

return x*y;

}const int n=100010,mod=1e9+1;

int n,h[30],a[30][30],f[30][100000];ll as=1;//h[0]:一共有多少行 h[i]:第i行有多少數

vectorb[30];

bool vis[n];

il void build(ri x)

}il bool ck(ri x,ri ct)

} ll ret=0;

go(j,0,(int)b[h[0]].size()-1)

return ret;

}int main()

printf("%lld\n",as);

return 0;

}

HNOI2012 集合選數(狀壓DP 構造)

題目要求若出現x,則不能出現2x,3x 所以我們考慮構造乙個矩陣 1 2 4 8 3 6 12 24 9 18 36 不難發現,對於乙個矩陣,若我選擇了乙個數x,則在矩陣內該數的相鄰格仔都不能選,題目就被轉化成了玉公尺田了,可以用狀壓dp解決 但是直接做是不對的,比如5就沒有出現在這個序列中 所以我...

HNOI2012 集合選數

這是題目 大概就是讓你找方案數嘛。開始我還以為是一道規律題,然後有愉快地打了乙個暴搜打表。找了十分鐘沒找出來。www.oeis.org。結果.這是表 半點規律沒有。然後想了想dp,想不出線性或帶log的。最後實在做不出來了,於是問了個大犇。大犇說這道題要用矩形。尼瑪沒在逗我。把題目意思轉換一下嘛。就...

題解 HNOI2012 集合選數

題目傳送門 直接看題面吧。感覺挺水的一道題啊?怎麼評到紫色的啊?考試的時候ljs出了這個題的加強版我就只想出這個思路,然後就爆了。不難發現,我們可以構造矩陣 x 2x 4x 6x 3x 6x 12x 24x 48x 9x 18x 36x 然後實際上就相當於在這個矩陣中選出一些數使得兩兩不相鄰。因為行...