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 }
posted @ 2018-02-28 14:27  阿波罗2003  阅读(...)  评论(... 编辑 收藏