题目大意
略
准备工作
已知原序列为 a1, a2……an。
设 b1, b2……bp 为原序列的一个子序列。
定义 Ans(b1, b2……bp) 为对应序列的完美子序列种数。
定义 Sum(b1, b2……bp) 为对应序列的所有非空子序列的整数之和。
定义 Sum[b1, b2……bp] = b1 + b2……+ bp。
找一下规律:Sum(b1, b2……bp) = 2p-1 * Sum[b1, b2……bp]。
分析1
首先说一下,分析1是为分析2服务的,分析1给出的代码很暴力,不仅超时,还超内存,但是最容易理解。
一开始我的DP思路是搞一个二维数组,即 dp[i][j] 表示前 i 个元素中,满足序列长度为 j 的完美序列个数。如果我这样定义,那么答案就为$\sum_{i = 1}^n dp[n][i]$。然而转移方程根本不晓得怎么推,还是得把模 m 余数为 0 的子序列都找出来,并且你在算完 dp[i][j] 之后算 dp[i + 1][j + 1] 的时候,之前某些模 m 余数不为 0 的子序列在加入 a[i + 1] 之后就有可能为 0,因此子序列模 m 的余数也应该作为一个维度。
重新定义一下dp数组:dp[i][j][k],表示前 i 个元素中,满足序列长度为 j ,模 m 余数为 k 的子序列个数。这样的话答案就为$\sum_{j = 1}^n dp[n][j][0]$。
在这个定义下,如果我们已经算出来了 dp[i][j][k],那么对于 a[i + 1],有拿和不拿两种选择,对应递推公式如下:
- 拿:dp[i + 1][j + 1][(2 * k + a[i+1] * 2j) % m] += dp[i][j][k]。
- 不拿:dp[i + 1][j][k] = dp[i][j][k]。
初始 dp[0][0][0] = 1,因为 m | 0 。
为了减少空间占用,可以使用滚动数组。
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INIT() std::ios::sync_with_stdio(false);std::cin.tie(0); 5 #define Rep(i,n) for (int i = 0; i < (n); ++i) 6 #define For(i,s,t) for (int i = (s); i <= (t); ++i) 7 #define rFor(i,t,s) for (int i = (t); i >= (s); --i) 8 #define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i) 9 #define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i) 10 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 11 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) 12 13 #define pr(x) cout << #x << " = " << x << " " 14 #define prln(x) cout << #x << " = " << x << endl 15 16 #define LOWBIT(x) ((x)&(-x)) 17 18 #define ALL(x) x.begin(),x.end() 19 #define INS(x) inserter(x,x.begin()) 20 21 #define ms0(a) memset(a,0,sizeof(a)) 22 #define msI(a) memset(a,inf,sizeof(a)) 23 #define msM(a) memset(a,-1,sizeof(a)) 24 #define mcy(d,s) memcpy(d, s, sizeof(s)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 31 template<typename T1, typename T2> 32 istream &operator>>(istream &in, pair<T1, T2> &p) { 33 in >> p.first >> p.second; 34 return in; 35 } 36 37 template<typename T> 38 istream &operator>>(istream &in, vector<T> &v) { 39 for (auto &x: v) 40 in >> x; 41 return in; 42 } 43 44 template<typename T1, typename T2> 45 ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) { 46 out << "[" << p.first << ", " << p.second << "]" << "\n"; 47 return out; 48 } 49 50 inline int gc(){ 51 static const int BUF = 1e7; 52 static char buf[BUF], *bg = buf + BUF, *ed = bg; 53 54 if(bg == ed) fread(bg = buf, 1, BUF, stdin); 55 return *bg++; 56 } 57 58 inline int ri(){ 59 int x = 0, f = 1, c = gc(); 60 for(; c<48||c>57; f = c=='-'?-1:f, c=gc()); 61 for(; c>47&&c<58; x = x*10 + c - 48, c=gc()); 62 return x*f; 63 } 64 65 typedef long long LL; 66 typedef unsigned long long uLL; 67 typedef pair< double, double > PDD; 68 typedef pair< int, int > PII; 69 typedef pair< string, int > PSI; 70 typedef set< int > SI; 71 typedef vector< int > VI; 72 typedef map< int, int > MII; 73 typedef pair< LL, LL > PLL; 74 typedef vector< LL > VL; 75 typedef vector< VL > VVL; 76 const double EPS = 1e-10; 77 const LL inf = 0x7fffffff; 78 const LL infLL = 0x7fffffffffffffffLL; 79 const LL mod = 1e9 + 7; 80 const int maxN = 5e3 + 7; 81 const LL ONE = 1; 82 const LL evenBits = 0xaaaaaaaaaaaaaaaa; 83 const LL oddBits = 0x5555555555555555; 84 85 // Calculate x^y % p 86 inline LL pow_mod(LL x, LL y, LL p = mod){ 87 LL ret = 1; 88 while(y){ 89 if(y & 1) ret = (ret * x) % p; 90 x = (x * x) % p; 91 y >>= 1; 92 } 93 return ret; 94 } 95 96 int n, m, a[maxN]; 97 LL ans; 98 99 // dp[i][j][k] 表示在前i个元素组成的序列中,选择了j个数,结果为k(mod m)的序列种数 100 LL dp[2][maxN][maxN]; 101 102 int main(){ 103 INIT(); 104 cin >> n >> m; 105 For(i, 1, n) cin >> a[i]; 106 107 int now; //< 滚动数组标识 108 dp[0][0][0] = 1; //< 算上空序列,方便计算 109 For(i, 1, n) { 110 now = i % 2; 111 mcy(dp[now], dp[!now]); 112 For(j, 0, n) { // 枚举子数列长度 113 Rep(k, m) { // 枚举余数 114 // 选a[i] 115 int tmp = (2 * k + a[i] * pow_mod(2, j, m)) % m; //< 产生新的余数 116 dp[now][j + 1][tmp] += dp[!now][j][k]; 117 // 不选a[i]不必讨论,因为 mcy 函数复制的同时搞定了这个分支 118 // dp[now][j][k] = dp[!now][j][k]; 119 } 120 } 121 } 122 123 For(j, 1, n) ans = (ans + dp[now][j][0]) % mod; 124 cout << ans << endl; 125 return 0; 126 }
分析2
分析2提供了两点优化,但程序仍然通过不了(爆内存),分析3将讨论如何降低空间复杂度。
设 x 为 m 中质因子 2 的个数。
由于 m <= 5000,所以 0 <= x <= 12。
则 m 可写成:
$$
\begin{align*}
m &= 2^0 * q_0 \\
&= 2^1 * q_1 \\
&……… \\
&……… \\
&= 2^x * q_x
\end{align*}
$$
如果序列 (b1, b2……bp) 是完美的,一定有 m | Sum(b1, b2……bp),也就是说一定有:
$$
\begin{align*}
&2^0 | 2^{p-1}\quad and\quad q_0 | Sum[b1, b2……bp] \\
or\quad &2^1 | 2^{p-1}\quad and\quad q_1 | Sum[b1, b2……bp] \\
or\quad &……………………………… \\
or\quad &……………………………… \\
or\quad &2^x | 2^{p-1}\quad and\quad q_x | Sum[b1, b2……bp]
\end{align*}
$$
上面至少有一个式子是成立的。
可以发现,当 p > x 时,要证明序列 (b1, b2……bp) 是完美的,只要$q_x | Sum[b1, b2……bp]$成立即可。
又,在 0 <= p <= x 时,要证明序列 (b1, b2……bp) 是完美的,只要$q_p | Sum[b1, b2……bp]$成立即可。
因此,当 p > x 时,只要讨论 $m = 2^x * q_x$的拆分情况即可;当 0 <= p <= x 时,只要讨论 $m = 2^p * q_p$ 的拆分情况即可。
也就是说,对于每一个子序列,都有唯一一个判定式$q_j | Sum[b1, b2……bp]$(0 <= j <= x) 能判断这个序列是否是完美的。
于是 dp 数组的第三维就不用算出整个和然后模 m 了,而只要算出 Sum[b1, b2……bp] 然后去模对应的 qj 即可。
以上是第一个优化。
第二个优化是关于枚举余数部分,其实并没有必要从 0 枚举到 m - 1,只需要枚举到 min(m, qj * 2) 即可,由于第一个优化的关系,随着 j 的增加, qj 两倍两倍地减小,上一级产生的余数刚好为这一级的两倍,所以在不超过 m 的情况下,只需枚举到 qj * 2。
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INIT() std::ios::sync_with_stdio(false);std::cin.tie(0); 5 #define Rep(i,n) for (int i = 0; i < (n); ++i) 6 #define For(i,s,t) for (int i = (s); i <= (t); ++i) 7 #define rFor(i,t,s) for (int i = (t); i >= (s); --i) 8 #define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i) 9 #define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i) 10 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 11 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) 12 13 #define pr(x) cout << #x << " = " << x << " " 14 #define prln(x) cout << #x << " = " << x << endl 15 16 #define LOWBIT(x) ((x)&(-x)) 17 18 #define ALL(x) x.begin(),x.end() 19 #define INS(x) inserter(x,x.begin()) 20 21 #define ms0(a) memset(a,0,sizeof(a)) 22 #define msI(a) memset(a,inf,sizeof(a)) 23 #define msM(a) memset(a,-1,sizeof(a)) 24 #define mcy(d,s) memcpy(d, s, sizeof(s)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 31 template<typename T1, typename T2> 32 istream &operator>>(istream &in, pair<T1, T2> &p) { 33 in >> p.first >> p.second; 34 return in; 35 } 36 37 template<typename T> 38 istream &operator>>(istream &in, vector<T> &v) { 39 for (auto &x: v) 40 in >> x; 41 return in; 42 } 43 44 template<typename T1, typename T2> 45 ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) { 46 out << "[" << p.first << ", " << p.second << "]" << "\n"; 47 return out; 48 } 49 50 inline int gc(){ 51 static const int BUF = 1e7; 52 static char buf[BUF], *bg = buf + BUF, *ed = bg; 53 54 if(bg == ed) fread(bg = buf, 1, BUF, stdin); 55 return *bg++; 56 } 57 58 inline int ri(){ 59 int x = 0, f = 1, c = gc(); 60 for(; c<48||c>57; f = c=='-'?-1:f, c=gc()); 61 for(; c>47&&c<58; x = x*10 + c - 48, c=gc()); 62 return x*f; 63 } 64 65 typedef long long LL; 66 typedef unsigned long long uLL; 67 typedef pair< double, double > PDD; 68 typedef pair< int, int > PII; 69 typedef pair< string, int > PSI; 70 typedef set< int > SI; 71 typedef vector< int > VI; 72 typedef map< int, int > MII; 73 typedef pair< LL, LL > PLL; 74 typedef vector< LL > VL; 75 typedef vector< VL > VVL; 76 const double EPS = 1e-10; 77 const LL inf = 0x7fffffff; 78 const LL infLL = 0x7fffffffffffffffLL; 79 const LL mod = 1e9 + 7; 80 const int maxN = 5e3 + 7; 81 const LL ONE = 1; 82 const LL evenBits = 0xaaaaaaaaaaaaaaaa; 83 const LL oddBits = 0x5555555555555555; 84 85 // 计算x的二进制位数 86 inline int getBits(LL x) { 87 int cnt = 1; 88 while(x >>= 1) ++cnt; 89 return cnt; 90 } 91 92 int n, m, a[maxN]; 93 LL ans; 94 95 // dp[i][j][k] 表示在前i个元素组成的序列中,选择了j个数,结果为k(mod m)的序列种数 96 LL dp[2][maxN][maxN]; 97 // pow2[i] = 2^i 98 int pow2[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; 99 100 int main(){ 101 INIT(); 102 cin >> n >> m; 103 For(i, 1, n) cin >> a[i]; 104 105 int x = getBits(LOWBIT(m)) - 1; //< x = m中质因子2的个数 106 int now; //< 滚动数组标识 107 108 dp[0][0][0] = 1; //< 算上空序列,方便计算 109 For(i, 1, n) { 110 now = i % 2; 111 mcy(dp[now], dp[!now]); 112 For(j, 0, n) { // 枚举子数列长度 113 int q = m / pow2[x]; 114 if(j < x) q = m / pow2[j]; 115 Rep(k, min(m, q << 1)) { // 枚举余数 116 // 选a[i] 117 int tmp = (k + a[i]) % q; 118 if(j < x) dp[now][j + 1][tmp] += dp[!now][j][k]; 119 else dp[now][j + 1][tmp] += dp[!now][j][k]; 120 // 不选a[i]不必讨论,因为 mcy 函数复制的同时搞定了这个分支 121 // dp[now][j][k] = dp[!now][j][k]; 122 } 123 } 124 } 125 126 For(j, 1, n) ans = (ans + dp[now][j][0]) % mod; 127 cout << ans << endl; 128 return 0; 129 }
分析3
分析2中,当 p >= x 时,序列的完美只与$q_x | Sum[b1, b2……bp]$有关,即不同 dp[i][j][k] (x <= j <= n)的 k 都是序列对 qx 取模得到的,余数相同是可以合并的,并不需要专门有个 j 来标识不同,因此可以用 dp[i][x][k] 来代表 $\sum_{j = x}^n dp[i][j][k]$。于是,dp数组就有如下分段定义:
- 当 0 <= j < x,时,dp[i][j][k] 表示前 i 个元素中,满足序列长度为 j ,模 qj 余数为 k 的子序列个数。
- 当 j == x,时,dp[i][j][k] 表示前 i 个元素中,满足所有序列长度大于等于 j ,模 qx 余数为 k 的子序列个数。
这里只叙述一下 j == x 时的状态转移方程:
- 拿:dp[i + 1][j][(k + a[i+1]) % qx] += dp[i][j][k]。
- 不拿:dp[i + 1][j][k] = dp[i][j][k]。
PS:x 是能等于 0 的,所以在计算答案时要变化一下。
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INIT() std::ios::sync_with_stdio(false);std::cin.tie(0); 5 #define Rep(i,n) for (int i = 0; i < (n); ++i) 6 #define For(i,s,t) for (int i = (s); i <= (t); ++i) 7 #define rFor(i,t,s) for (int i = (t); i >= (s); --i) 8 #define ForLL(i, s, t) for (LL i = LL(s); i <= LL(t); ++i) 9 #define rForLL(i, t, s) for (LL i = LL(t); i >= LL(s); --i) 10 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 11 #define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i) 12 13 #define pr(x) cout << #x << " = " << x << " " 14 #define prln(x) cout << #x << " = " << x << endl 15 16 #define LOWBIT(x) ((x)&(-x)) 17 18 #define ALL(x) x.begin(),x.end() 19 #define INS(x) inserter(x,x.begin()) 20 21 #define ms0(a) memset(a,0,sizeof(a)) 22 #define msI(a) memset(a,inf,sizeof(a)) 23 #define msM(a) memset(a,-1,sizeof(a)) 24 #define mcy(d,s) memcpy(d, s, sizeof(s)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 31 template<typename T1, typename T2> 32 istream &operator>>(istream &in, pair<T1, T2> &p) { 33 in >> p.first >> p.second; 34 return in; 35 } 36 37 template<typename T> 38 istream &operator>>(istream &in, vector<T> &v) { 39 for (auto &x: v) 40 in >> x; 41 return in; 42 } 43 44 template<typename T1, typename T2> 45 ostream &operator<<(ostream &out, const std::pair<T1, T2> &p) { 46 out << "[" << p.first << ", " << p.second << "]" << "\n"; 47 return out; 48 } 49 50 inline int gc(){ 51 static const int BUF = 1e7; 52 static char buf[BUF], *bg = buf + BUF, *ed = bg; 53 54 if(bg == ed) fread(bg = buf, 1, BUF, stdin); 55 return *bg++; 56 } 57 58 inline int ri(){ 59 int x = 0, f = 1, c = gc(); 60 for(; c<48||c>57; f = c=='-'?-1:f, c=gc()); 61 for(; c>47&&c<58; x = x*10 + c - 48, c=gc()); 62 return x*f; 63 } 64 65 typedef long long LL; 66 typedef unsigned long long uLL; 67 typedef pair< double, double > PDD; 68 typedef pair< int, int > PII; 69 typedef pair< string, int > PSI; 70 typedef set< int > SI; 71 typedef vector< int > VI; 72 typedef map< int, int > MII; 73 typedef pair< LL, LL > PLL; 74 typedef vector< LL > VL; 75 typedef vector< VL > VVL; 76 const double EPS = 1e-10; 77 const LL inf = 0x7fffffff; 78 const LL infLL = 0x7fffffffffffffffLL; 79 const LL mod = 1e9 + 7; 80 const int maxN = 5e3 + 7; 81 const LL ONE = 1; 82 const LL evenBits = 0xaaaaaaaaaaaaaaaa; 83 const LL oddBits = 0x5555555555555555; 84 85 // 计算x的二进制位数 86 inline int getBits(LL x) { 87 int cnt = 1; 88 while(x >>= 1) ++cnt; 89 return cnt; 90 } 91 92 int n, m, a[maxN]; 93 LL ans; 94 LL dp[2][13][maxN]; 95 // pow2[i] = 2^i 96 int pow2[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; 97 98 int main(){ 99 INIT(); 100 cin >> n >> m; 101 For(i, 1, n) cin >> a[i]; 102 103 int x = getBits(LOWBIT(m)) - 1; //< x = m中质因子2的个数 104 int now; //< 滚动数组标识 105 106 dp[0][0][0] = 1; //< 算上空序列,方便计算 107 For(i, 1, n) { 108 now = i % 2; 109 mcy(dp[now], dp[!now]); 110 For(j, 0, min(x, i)) { 111 int q = m / pow2[j]; 112 Rep(k, min(m, q << 1)) { //< k表示可能用到的余数 113 if(dp[!now][j][k]) { 114 int tmp = (k + a[i]) % q; //< 产生新余数 115 // j < x 代表取了 j 个的情况 116 // j == x 代表取了 j 个以上的情况 117 if(j < x) dp[now][j + 1][tmp] += dp[!now][j][k] % mod; 118 else dp[now][j][tmp] += dp[!now][j][k] % mod; 119 } 120 } 121 } 122 } 123 // 这里做了一下改变,因为x可能等于0,如果从1开始加的话会漏掉很多情况 124 For(j, 0, x) ans = (ans + dp[now][j][0]) % mod; 125 cout << ans - 1 << endl; //< 最后要减去空序列的一种 126 return 0; 127 } 128 129 /* 130 15 11 131 1 2 5 7 8 4 3 7 8 3 3 9 9 7 5 132 2979 133 134 16 8 135 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 136 64839 137 138 15 6 139 1 2 5 7 8 4 3 7 8 3 3 9 9 7 5 140 6547 141 142 15 6 143 1 2 5 7 8 4 3 7 8 3 3 9 9 7 5 144 10938 145 */