SPOJ

来源:互联网 发布:matlab求协方差矩阵 编辑:程序博客网 时间:2024/06/08 12:16

传送门:SPOJ TLE

题意:给出长度为n的序列c,求非负整数序列a,满足a<2^m,并且有a[i]&a[i+1]=0,对于每个a[i],要保证a[i]不是c[i]的倍数,求这样的a[i]序列的个数

思路:dp[i][j]表示长度为i以j结尾的序列的个数,可以第一个条件和第三个条件都好处理,关键是第二个,可以发现dp[i][j] = ∑(dp[i - 1][k] * (k & j == 0)), 那么可以转化为~j & k == k,即k为~j的子集,也就是说dp[i][j] = dp[i - 1][~j] = dp[i - 1][((1 << m) - 1) ^ j]. 所以每次我们算完

dp[i][1..n]后,要对每个dp[i][j]求j的所有子集的和,留作下一次转移用。

dalao思路 + 精简代码:点击打开链接

代码:

#include<bits/stdc++.h>#define ll long long#define inf 0x3f3f3f3fusing namespace std;typedef pair<int,int> P;const int MAXN = 100010;const int mod = 1e9;ll dp[MAXN], sum[MAXN];int c[55];int main(){int T, n, m;cin >> T;while(T--){scanf("%d %d", &n, &m);for(int i = 1; i <= n; i++)scanf("%d", c + i);int up = 1 << m;for(int i = 0; i < up; i++){if(i % c[1])dp[i] = sum[i] = 1;elsedp[i] = sum[i] = 0;}for(int j = 0; j < m; j++)for(int k = 0; k < up; k++)if(k & 1 << j)sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod;//求k的子集的和for(int i = 2; i <= n; i++){for(int j = 0; j < up; j++){if(j % c[i])dp[j] = sum[(up - 1) ^ j];elsedp[j] = 0;}for(int j = 0; j < up; j++)sum[j] = dp[j];for(int j = 0; j < m; j++)for(int k = 0; k < up; k++)if(k & 1 << j)sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod;}ll ans = 0;for(int i = 0; i < up; i++)ans = (ans + dp[i]) % mod;cout << ans << endl;} return 0;}

第一次做高维前缀和的题,个人理解:高维前缀和可以解决状压表示的一类集合之间的前缀和问题,例如求某个集合所有子集对应的值的和,或者某个集合的所有超集和。

求超集和:

for(int i = 0; i < m; i++)for(int j = 0; j < (1 << m); j++)if(!(j & 1 << i))dp[i] += dp[j | (1 << i)];

求子集和:

for(int j = 0; j < m; j++)for(int k = 0; k < (1 << m); k++)if(k & 1 << j)sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod;//求k的子集的和






原创粉丝点击