bzoj2287
背包+fft
既然要不选一个东西,那么我们求出前缀背包和后缀背包,每次答案就是f[i-1][w]*g[i+1][j-w]
但是这样复杂度还是n^3,跑不过,但是我们发现上面那个东西不就是个裸卷积吗,直接上fft,但是wa了...
wa的程序,大概是精度问题吧
#include<bits/stdc++.h> using namespace std; #define pi acos(-1) const int N = 3010; int n, m, L, x, nn, mm; int r[N * 4], f[N][N], g[N][N], w[N]; complex<double> a[N * 4], b[N * 4]; void fft(complex<double> *a, int f) { for(int i = 0; i < n; ++i) if(i < r[i]) swap(a[i], a[r[i]]); for(int i = 1; i < n; i <<= 1) { complex<double> t(cos(pi / i), f * sin(pi / i)); for(int p = i << 1, j = 0; j < n; j += p) { complex<double> w(1, 0); for(int k = 0; k < i; ++k, w *= t) { complex<double> x = a[j + k], y = w * a[j + k + i]; a[j + k] = x + y; a[j + k + i] = x - y; } } } } int main() { scanf("%d%d", &nn, &mm); for(int i = 1; i <= nn; ++i) scanf("%d", &w[i]); f[0][0] = 1; for(int i = 1; i <= nn; ++i) for(int j = 0; j <= mm; ++j) { f[i][j] = f[i - 1][j]; if(j >= w[i]) f[i][j] = (f[i][j] + f[i - 1][j - w[i]]) % 10; } g[nn + 1][0] = 1; for(int i = nn; i; --i) for(int j = 0; j <= mm; ++j) { g[i][j] = g[i + 1][j]; if(j >= w[i]) g[i][j] = (g[i][j] + g[i + 1][j - w[i]]) % 10; } for(int i = 1; i <= nn; ++i) { for(int j = 1; j <= mm; ++j) { L = 0; m = 2 * j + 2; for(int k = 0; k <= m; ++k) a[k] = b[k] = 0; for(int k = 0; k <= j; ++k) { a[k] = f[i - 1][k]; b[k] = g[i + 1][k]; } for(n = 1; n <= m; n <<= 1) ++L; for(int k = 0; k < n; ++k) r[k] = (r[k >> 1] >> 1) | ((k & 1) << (L - 1)); fft(a, 1); fft(b, 1); for(int k = 0; k <= n; ++k) a[k] = a[k] * b[k]; fft(a, -1); int ans = (int)(a[j].real() / (double)n + 0.5); printf("%d", ans % 10); } puts(""); } return 0; }
写了一个正解
f[i]:装满i的方案数
c[i][j]:装满j不用i的方案数
j<w[i],自然c[i][j]=f[j],因为w[i]装不下,不可能选
j>=w[i],c[i][j]=f[j]-c[i][j-w[i]],在j-w[i]填上一个w[i]就是j,表示选到第i个物品一定选了i的方案数,相减就是不选的方案数
#include<bits/stdc++.h> using namespace std; const int N = 2010; int n, m; int w[N], c[N], f[N]; int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d", &w[i]); f[0] = 1; for(int i = 1; i <= n; ++i) for(int j = m; j >= w[i]; --j) f[j] = (f[j] + f[j - w[i]]) % 10; for(int i = 1; i <= n; ++i) { for(int j = 0; j <= m; ++j) { if(j >= w[i]) c[j] = (f[j] - c[j - w[i]] % 10 + 10) % 10; else c[j] = f[j]; if(j > 0) printf("%d", c[j]); } puts(""); } return 0; }