求對於正整數\(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 然後實際上就相當於在這個矩陣中選出一些數使得兩兩不相鄰。因為行...