uoj 300 [CTSC2017]吉夫特 - Lucas - 分块 - 动态规划
题目传送门
戳此处转移
题目大意
给定一个长为$n$的序列,问它有多少个长度大于等于2的子序列$b_{1}, b_{2}, \cdots, b_{k}$满足$\prod_{i = 2}^{k}C_{b_{i - 1}}^{b_{i}} \equiv 1 \pmod{2}$。答案模$10^{9} + 7$
考虑限制条件,即前后两个数$b_{i - 1}, b_{i}$,它们要满足$C_{b_{i - 1}}^{b_{i}} \equiv 1\pmod{2}$。
这样不好处理,考虑使用Lucas定理,得到$b_{i - 1}$是$b_{i}$的子集的结论。
然后是个常规动态规划,用$f[i][s]$表示考虑到第$i$位,最后一个数是$s$的方案数。但是这样时间复杂度$O(n^{2})$。
考虑分块,每个位置将它的子集信息上传。
然后修改和查询一个枚举前9位,一个枚举后9位就行了。
一直不知道所有数互不相同的意义。
然后直到今天,发现可以直接枚举子集,$O(3^{\left \lceil \log_{2}W \right \rceil})$。
Code
1 /** 2 * uoj 3 * Problem#300 4 * Accepted 5 * Time: 400ms 6 * Memory: 2956k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int S = 1 << 9, M = 1e9 + 7; 13 const int maskL = (1 << 9) - 1, maskH = maskL << 9, mask = maskL | maskH; 14 15 int n; 16 int *ar; 17 int f[S][S]; 18 19 inline void init() { 20 scanf("%d", &n); 21 ar = new int[(n + 1)]; 22 for (int i = 1; i <= n; i++) 23 scanf("%d", ar + i); 24 } 25 26 inline int query(int S) { 27 int rt = 0, s0 = S & maskL, s1 = (S & maskH) >> 9, ms1 = s1 ^ maskL; 28 for (int s = ms1; s; s = (s - 1) & ms1) 29 rt = (rt + f[s | s1][s0]) % M; 30 return (rt + f[s1][s0]) % M; 31 } 32 33 inline void modify(int S, int val) { 34 int s0 = S & maskL, s1 = (S & maskH) >> 9; 35 for (int s = s0; s; s = (s - 1) & s0) 36 f[s1][s] = (f[s1][s] + val) % M; 37 f[s1][0] = (f[s1][0] + val) % M; 38 } 39 40 int res = 0; 41 42 inline void solve() { 43 modify(mask, 1); 44 for (int i = 1, c; i <= n; i++) { 45 c = query(ar[i]); 46 res = (res + c) % M; 47 modify(ar[i], c); 48 } 49 res = (res - n + M) % M; 50 printf("%d", res); 51 } 52 53 int main() { 54 // freopen("gift.in", "r", stdin); 55 init(); 56 solve(); 57 return 0; 58 }