《集合論與圖論》這門課程有一道作業題,要求同學們求出的所有滿足以 下條件的子集:若 x 在該子集中,則 2x 和 3x 不能在該子集中。同學們不喜歡這種具有列舉性 質的題目,於是把它變成了以下問題:對於任意乙個正整數 n≤100000,如何求出 的滿足上述約束條件的子集的個數(只需輸出對 1,000,000,001 取模的結果),現在這個問題就 交給你了。
只有一行,其中有乙個正整數 n,30%的資料滿足 n≤20。
僅包含乙個正整數,表示有多少個滿足上述約束條件 的子集。
4
8
有8 個集合滿足要求,分別是空集,,,,,,,。狀態壓縮dp
這道題目的思路真的很巧。我們可以構造乙個矩陣如下x3x
9x27 x
2x6x
18x54x
4x12x
36 x
108 x
8x24x
72x216 x
此時令x=
1 ,我們可以得到13
92726
1854412
36108824
72216
我們可以觀察到,每個數和他相鄰的數都不可同時取,可以計算出本矩陣中取數的方案數。
但是我們會發現漏了5和7,那麼按照上面進行構造。
計算出所有矩陣的結果,因為不同矩陣間的數是一定可以共同存在的,此時乘法原理,將各矩陣求得的方案數相乘取模即為答案。
如何統計方案數 f[
i][j
] 表示當前處理到第
i 行,本行的狀態為
j。那麼看一下j&(j>>1),j&k(k為上一行的某狀態)是否都為0,如果是那麼就從f[
i−1]
[k] 轉移而來。 f[
i][j
]=∑(
f[i−
1][k
]|ki
sok)
。
#include
#include
#include
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
typedef
long
long ll;
const
int mod = 1000000001;
const
int size = 20+1;
ll ans=1;
int n;
int a[size][size],b[size],f[size][2049];
bool mark[100005];
inline
int read(int &in)
inline
int dp(int x)
for(int i=0;i<=18;i++)
for(int j=0;j<=b[i];j++)
f[i][j]=0;
f[0][0]=1;
for(int i=0;i<18;i++)
for(int j=0;j<=b[i];j++)
if(f[i][j])
for(int k=0;k<=b[i+1];k++)
if(((j&k)==0) && ((k&(k>>1))==0))
f[i+1][k]=(f[i][j]+f[i+1][k])%mod;
return f[18][0];
}int main()
bzoj 2734 集合選數
構造矩陣 1 3 9 27 2 6 18 54 4 12 36 108 每個數是上面的數乘2,左面的數乘3。這樣進行狀壓dp就是相鄰的格仔不能選的方案數。如何判斷乙個二進位制數沒有兩個連續的1?x x 1 0 如果有的數沒有出現過,就以它為左上角元素再構造乙個矩陣。這是怎麼想到的?include i...
bzoj 2734 集合選數
written with stackedit.集合論與圖論 這門課程有一道作業題,要求同學們求出 的所有滿足以 下條件的子集 若 x 在該子集中,則 2x 和 3x 不能在該子集中。同學們不喜歡這種具有列舉性 質的題目,於是把它變成了以下問題 對於任意乙個正整數 n leq 100000 如何求出 ...
BZOJ 2734 集合選數(狀態壓縮DP)
題意 給出乙個由1到n的數字組成的集合。定義合法子集為若x在子集中則2x 3x均不能在子集中。求有多少個合法的子集。思路 1 3 9 2 6 18 4 12 36 對於上面的矩陣,我們發現就等價於不選相鄰數字的方案數。因此列舉每個還沒有用到的數字,建立以該數字為左上角的矩陣。接著就是狀態壓縮dp。i...