本文主要是介绍bzoj2734 集合选数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
集合选数
题目背景:
bzoj2734
分析:构造 + 状态压缩DP
表示思路比较清奇啊·····我是没有想到的······我们来考虑如何选择可行的数字,先构造一个矩阵
1 3 9 27 81 243
2 6 18 54 162 486
4 12 36 108 324 972
8 24 72 216 648 1944
16 48 144 432 1296 3888
...
显然我们可以发现这个矩阵中,相邻的数字(上下或者左右)是都不能被选择的,然后每一行做多只会有11个数,那么我们可以选择直接状压每一行然后DP方案数即可,但是我们可以发现,上面这个矩阵中有些数字是没有出现的,出现的为2m3n形式,那么说明我们的矩阵左上角不一定只能为1,还可以为很多其他的数字,例如5,7,稍加分析就可以知道每一次取在之前的矩阵中没有出现过的最小的数字构造新的矩阵就可以了,并且,这样选择的数字,不同的矩阵中不会出现相同的数(质因子不同)所以直接利用乘法原理相乘即可。
Source:
/*created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>inline char read() {static const int IN_LEN = 1024 * 1024;static char buf[IN_LEN], *s, *t;if (s == t) {t = (s = buf) + fread(buf, 1, IN_LEN, stdin);if (s == t) return -1;}return *s++;
}///*
template<class T>
inline void R(T &x) {static char c;static bool iosig;for (c = read(), iosig = false; !isdigit(c); c = read()) {if (c == -1) return ;if (c == '-') iosig = true; }for (x = 0; isdigit(c); c = read()) x = ((x << 2) + x << 1) + (c ^ '0');if (iosig) x = -x;
}
//*/const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;*oh++ = c;
}template<class T>
inline void W(T x) {static int buf[30], cnt;if (x == 0) write_char('0');else {if (x < 0) write_char('-'), x = -x;for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;while (cnt) write_char(buf[cnt--]);}
}inline void flush() {fwrite(obuf, 1, oh - obuf, stdout);
}/*
template<class T>
inline void R(T &x) {static char c;static bool iosig;for (c = getchar(), iosig = false; !isdigit(c); c = getchar())if (c == '-') iosig = true; for (x = 0; isdigit(c); c = getchar()) x = ((x << 2) + x << 1) + (c ^ '0');if (iosig) x = -x;
}
//*/const int MAXN = 18;
const int mod = 1000000000 + 1;
const int MAXX = 100000 + 10;long long n, ans;
long long matrix[MAXN][MAXN];
long long f[MAXN][1 << MAXN | 1];
bool vis[MAXX];inline long long calc(int x) {int r;static int line[MAXN];matrix[1][1] = x, vis[x] = true;for (int i = 1; ; ++i) {if (i != 1) {matrix[i][1] = matrix[i - 1][1] * 2;if (matrix[i][1] > n) {r = i - 1;break ;} else vis[matrix[i][1]] = true;}for (int j = 2; ; ++j) {matrix[i][j] = matrix[i][j - 1] * 3;if (matrix[i][j] > n) {line[i] = j - 1;break ;} else vis[matrix[i][j]] = true;}}line[r + 1] = 0;for (int i = 0; i <= r + 1; ++i)for (int j = 0; j < (1 << line[i]); ++j)f[i][j] = 0;f[0][0] = 1;for (int i = 0; i <= r; ++i)for (int j = 0; j < (1 << line[i]); ++j) {if (f[i][j]) {if (j & (j >> 1)) continue ;for (int k = 0; k < (1 << line[i + 1]); ++k) {if (k & j) continue ;f[i + 1][k] = (f[i + 1][k] + f[i][j]) % mod;}}}return f[r + 1][0];
}int main() {R(n), ans = 1;for (int i = 1; i <= n; ++i) if (!vis[i]) ans = ans * calc(i) % mod;std::cout << ans;return 0;
}
这篇关于bzoj2734 集合选数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!