题目:
有 n n n个不同的数,可以取出一个,两个,或三个相加。问各种价值的方案数(顺序不同算一种)。
思路:
设
A
A
A为每种物品只选一个的生成函数,
B
B
B为每种物品选两个的生成函数,
C
C
C为每种物品选三个的生成函数。因为算价值的和,所以
x
x
x的幂表示价值。则
- 选一个物品的生成函数就是 A A A
- 选两个不同的物品的生成函数为 A 2 − B 2 \frac{A^2-B}{2} 2A2−B。 A 2 A^2 A2表示任选两个,但是要减掉两个物品相同的情况,即 B B B,然后因为不考虑顺序,除 2 2 2。
- 选三个不同物品的情况的生成函数为
A
3
−
3
A
B
+
2
C
6
\frac{A^3-3AB+2C}{6}
6A3−3AB+2C。首先上面已经算出了选两个的情况,所以
A
2
−
B
2
⋅
B
\frac{A^2-B}{2}\cdot B
2A2−B⋅B就是选了三个,但是会出现
(
X
,
Y
,
X
)
(
X
,
Y
,
Y
)
(X,Y,X)(X,Y,Y)
(X,Y,X)(X,Y,Y)这两种情况,这两种情况等于
A
B
−
C
AB-C
AB−C,减掉后等于
A
3
−
3
A
B
+
2
C
2
\frac{A^3-3AB+2C}{2}
2A3−3AB+2C,再消去顺序的影响除
3
3
3。
综上,最后的生成函数就是 A + A 2 − B 2 + A 3 − 3 A B + 2 C 6 A+\frac{A^2-B}{2}+\frac{A^3-3AB+2C}{6} A+2A2−B+6A3−3AB+2C,答案就是有多少项的系数不为 0 0 0。
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const double pi=acos(-1.0);
const int N = 262194;
int n, m, r[N];
struct complex {
double x, y;
complex (double xx = 0, double yy = 0) {
x = xx, y = yy;
}
}a[N], b[N], c[N], o[N], a_o[N];
complex operator + (complex a,complex b) { return complex(a.x + b.x, a.y + b.y); }
complex operator - (complex a,complex b) { return complex(a.x - b.x, a.y - b.y); }
complex operator * (complex a,complex b) { return complex(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); }
complex operator / (complex a,double b) { return complex(a.x / b, a.y / b); }
void init(int n) {
double t = 2 * pi / n;
for (int i = 0; i <= n; ++i) {
o[i] = complex(cos(2.0 * pi * i / n), sin(2.0 * pi * i / n));
a_o[i] = complex(cos(2.0 * pi * i / n),-1 * sin(2.0 * pi * i / n));
}
}
void fft(int n, complex *a, complex *w)
{
for (int i = 0; i < n; ++i)
if (i < r[i])
swap(a[i], a[r[i]]);
for (int i = 2; i <= n; i <<= 1) {
int m = i >> 1;
for (int j = 0; j < n;j += i)
for (int k = 0; k < m; ++k) {
complex t = a[j + m + k] * w[n / i * k];
a[j + m + k] = a[j + k] - t;
a[j + k] = a[j + k] + t;
}
}
}
main() {
static int n, m, fn, x, l=0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &x);
a[x].x += 1, b[x * 2].x += 1, c[x * 3].x += 1;
m = max(m, x * 3);
}
fn = 1;
while (fn <= m + m) fn <<= 1, ++l;
for (int i = 0; i < fn; ++i)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
init(fn);
fft(fn, a, o);
fft(fn, b, o);
fft(fn, c, o);
complex n2, n3, n6;
n2.x = 2, n2.y = 0;
n3.x = 3, n3.y = 0;
n6.x = 6, n6.y = 0;
for (int i = 0; i <= fn; ++i)
a[i] = ((a[i] * a[i] * a[i]) - n3 * a[i] * b[i] + n2 * c[i]) / 6.0 + (a[i] * a[i] - b[i]) / 2.0 + a[i];
fft(fn, a, a_o);
for (int i = 1; i <= m + m; ++i) {
long long ans = (long long) (a[i].x / fn + 0.5);
if (ans) printf("%d %d\n", i, ans);
}
}