1 3 9 ……
2 6 18……
4 12 36……
把数写成上面的样子,那么就变成了给出若干个类似矩阵的东西, 然后求任意一行r的任意一个元素c与r-1,r,r+1行的c-1,c+1不能同时被选中..就是一个状态压缩的dp.dp(x, s)表示第x行, 这一行状态为s的方案数.最后乘法原理乘一下。
----------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define b(i) (1 << (i))
const int MAXR = 18;
const int MAXC = 12;
const int maxn = 100009;
const int MOD = 1000000001;
int N, n, ans, dp[MAXR][b(MAXC)], A[MAXR][MAXC], len[MAXR];
bool F[maxn];
inline void upd(int &t, int v) {
if((t += v) >= MOD) t -= MOD;
}
void Calculate() {
int cnt = 0;
for(int i = b(len[0]); i--; )
dp[0][i] = !(i & (i >> 1));
for(int i = 1; i < n; i++) {
memset(dp[i], 0, sizeof dp[i]);
for(int s = b(len[i]); s--; )
for(int ps = b(len[i - 1]); ps--; )
if(!(s & (s >> 1)) && !(s & ps))
upd(dp[i][s], dp[i - 1][ps]);
}
for(int s = b(len[--n]); s--; )
upd(cnt, dp[n][s]);
ans = ll(ans) * cnt % MOD;
}
int main() {
scanf("%d", &N);
memset(F, 0, sizeof F);
ans = 1;
for(int i = 1; i <= N; i++) if(!F[i]) {
F[A[0][0] = i] = 1;
n = 0;
for(int &j = n; ; j++) {
len[j] = 1;
for(int &k = len[j]; ; k++) if(A[j][k - 1] * 3 <= N) {
F[A[j][k] = A[j][k - 1] * 3] = true;
} else
break;
if(A[j][0] << 1 > N) break;
F[A[j + 1][0] = A[j][0] << 1] = true;
}
n++;
Calculate();
}
printf("%d
", ans);
return 0;
}
----------------------------------------------------------------------------------
2734: [HNOI2012]集合选数
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 762 Solved: 447
[Submit][Status][Discuss]
Description
《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。
Input
只有一行,其中有一个正整数 n,30%的数据满足 n≤20。
Output
仅包含一个正整数,表示{1, 2,..., n}有多少个满足上述约束条件 的子集。
Sample Input
4
Sample Output
8
【样例解释】
有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
【样例解释】
有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。