蔬菜越来越多。
与非 $\mathcal{Link}$ link
$\mathcal{Sol}$ 记录一个常见(?)技巧。 当题目给定一个位运算操作时,先从每个数位入手,只考虑四种情况。 再看这个位运算操作是否可以推广。
拿这道题举例。
$$
not (A) = A \ nand \ A \\
A \ or \ B = not(not(A) \ nand \ not(B)) \\
A \ and \ B = not(A \ nand \ B) \\
A \ xor \ B = not(not(A) \ and \ not(B) \ or \ (A \ and \ B))
$$
就是说,只要有 nand 操作,你就可以把所有的操作全部表示出来。
$\mathcal{Code}$
nand.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <cstdio> #include <iostream> #include <algorithm> #define LL long long using namespace std;const int MAXN = 1e7 + 5 ;LL n, k; LL l, r, a[MAXN], b[MAXN], num[MAXN], ans; LL query (LL x) { LL res = 0 ; if (x >= ((1ll << k) - 1 )) return 1ll << num[k - 1 ]; for (int i = k - 1 ; i >= 0 && x >= 0 ; i--) { if ((x >> i) & 1 ) { if (b[i]) { res = (res + (1ll << num[i] - 1 )); x -= b[i]; } else { res = (res + (1ll << num[i])); break ; } } } res += (x == 0 ); return res; } int main () { scanf ("%lld %lld %lld %lld" , &n, &k, &l, &r); for (int i = 1 ; i <= n; i++) scanf ("%lld" , &a[i]); LL statment = 0 ; for (int i = k - 1 ; i >= 0 ; i--) if (!((statment >> i) & 1 )) { LL tmp = (1ll << k) - 1 ; for (int j = 1 ; j <= n; j++) if ((a[j] >> i) & 1 ) { tmp &= a[j]; } else tmp &= (~a[j]); b[i] = tmp; num[i] = 1 ; statment |= tmp; } for (int i = 1 ; i < k; i++) num[i] += num[i - 1 ]; printf ("%lld\n" , query (r) - query (l - 1 )); return 0 ; }
集合选数 $\mathcal{Sol}$ 属于神仙构造了。 考试时想到了要把建树,但是没把树拍扁。
首先一个 树形DP 的感觉不难得到。 然后构造如下的矩阵。
$$
1 \ 2 \ 4 \ 8 \ 16 \ 32 \ 64 \dots \\
3 \ 6 \ 12 \ 24 \ 48 \ 96 \ 192 \ 384 \dots \\
9 \ 18 \ 36 \ 72 \ 144 \ 288 \ 576 \ 1152 \dots \\
$$
形式化一点就是
$a_{i, j}=\left\{\begin{matrix} a_{i - 1, j} \times 3, j = 1 \\ a_{i, j - 1} \times 2, j \not= 1 \end{matrix}\right.$
矩阵很小,可以状压。
$\mathcal{Code}$
set.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define LL long long using namespace std;const int MAXN = 15 ;const int MAXM = 20 ;const int Mod = 1e9 + 1 ;int n, r, c[MAXM], mat[MAXN][MAXM];bool vis[(1 << MAXM) + 5 ], used[(1 << MAXM) + 5 ];LL dp[MAXN][(1 << MAXM) + 5 ], ans; LL cal (int s) { mat[1 ][1 ] = s; used[s] = 1 ; for (int i = 2 ; i <= 12 ; i++) { mat[i][1 ] = mat[i - 1 ][1 ] * 3 ; if (mat[i][1 ] > n) { r = i - 1 ; break ; } used[mat[i][1 ]] = 1 ; } for (int i = 1 ; i <= r; i++) { for (int j = 2 ; j <= 18 ; j++) { mat[i][j] = mat[i][j - 1 ] * 2 ; if (mat[i][j] > n) { c[i] = j - 1 ; break ; } used[mat[i][j]] = 1 ; } } for (int i = 0 ; i < (1 << c[1 ]); i++) dp[1 ][i] = vis[i]; for (int i = 2 ; i <= r; i++) { for (int j = 0 ; j < (1 << c[i]); j++) if (vis[j]) { dp[i][j] = 0 ; for (int k = 0 ; k < (1 << c[i - 1 ]); k++) if (vis[k] && (k & j) == 0 ) { dp[i][j] = (dp[i - 1 ][k] + dp[i][j]) % Mod; } } } LL sum = 0 ; for (int i = 0 ; i < (1 << c[r]); i++) sum = (sum + dp[r][i]) % Mod; return sum; } int main () { scanf ("%d" , &n); for (int i = 0 ; i < (1 << MAXM - 2 ); i++) if (!((i << 1 ) & i)) vis[i] = 1 ; ans = 1 ; for (int i = 1 ; i <= n; i++) if (!used[i]) ans = (ans * cal (i)) % Mod; printf ("%lld\n" , ans); return 0 ; }