概述
题目链接:【JSOI 2012】分零食
题目大意:将 m m 颗糖依次分给 个小朋友,每个小朋友的快乐度为 f(x=得到的糖果数)=ox2+sx+u f ( x = 得 到 的 糖 果 数 ) = o x 2 + s x + u 。特别地, f(0)=1 f ( 0 ) = 1 。糖果分完后剩下的小朋友将得不到糖果。求每种情况下小朋友快乐度乘积之和 mod998244353 mod 998244353 的结果。
令 g[i][j] g [ i ] [ j ] 表示前 i i 个小朋友得到 颗糖的答案。则: g[i][j]=∑jk=1f(j−k)g[i−1][k] g [ i ] [ j ] = ∑ k = 1 j f ( j − k ) g [ i − 1 ] [ k ] 。发现这是个卷积的形式。想到 FFT F F T ,发现 g[n]=fn g [ n ] = f n 。
令 A[n]=∑nj=1g[i] A [ n ] = ∑ j = 1 n g [ i ] 。我们使用倍增的方法求出 A[n] A [ n ] 。
A[n+1]=∑n+1i=1g[i]
A
[
n
+
1
]
=
∑
i
=
1
n
+
1
g
[
i
]
A[n+1]=A[n]+g[n+1]
A
[
n
+
1
]
=
A
[
n
]
+
g
[
n
+
1
]
A[2n]=∑2ni=1g[i]
A
[
2
n
]
=
∑
i
=
1
2
n
g
[
i
]
A[2n]=A[n]+∑2ni=n+1g[i]
A
[
2
n
]
=
A
[
n
]
+
∑
i
=
n
+
1
2
n
g
[
i
]
A[2n]=A[n]+fn∑ni=1fi
A
[
2
n
]
=
A
[
n
]
+
f
n
∑
i
=
1
n
f
i
A[2n]=A[n]+g[n]∗A[n]
A
[
2
n
]
=
A
[
n
]
+
g
[
n
]
∗
A
[
n
]
然后用 FFT F F T 做卷积即可。
#include <cmath>
#include <cstdio>
#include <complex>
#include <iostream>
using namespace std;
typedef complex<double> cd;
const double pi = acos(-1.);
const int maxn = 1 << 15 | 5;
cd f[maxn], g[maxn], dp[maxn], tmp[maxn];
int n, m, o, s, u, mod, lim, bit, r[maxn];
void fft(cd *a, int dft) {
for (int i = 0; i < lim; i++) {
if (i < r[i]) {
swap(a[i], a[r[i]]);
}
}
for (int k = 1; k < lim; k <<= 1) {
cd wn0(cos(pi / k), dft * sin(pi / k));
for (int i = 0; i < lim; i += k << 1) {
cd wnk(1, 0);
for (int j = i; j < i + k; j++, wnk *= wn0) {
cd x = a[j], y = wnk * a[j + k];
a[j] = x + y, a[j + k] = x - y;
}
}
}
if (dft == -1) {
for (int i = 0; i < lim; i++) {
a[i] /= lim;
a[i] = int(a[i].real() + 0.5) % mod;
}
}
}
void solve(cd *dp, cd *g, int n) {
if (!n) {
g[0] = 1;
return;
}
if (n & 1) {
solve(dp, g, n - 1);
fft(g, 1);
for (int i = 0; i < lim; i++) {
g[i] *= f[i];
}
fft(g, -1);
for (int i = 0; i <= m; i++) {
dp[i] += g[i];
dp[i] = int(dp[i].real() + 0.5) % mod;
}
for (int i = m + 1; i < lim; i++) {
g[i] = 0;
}
} else {
solve(dp, g, n >> 1);
for (int i = 0; i < lim; i++) {
tmp[i] = dp[i];
}
fft(tmp, 1);
fft(g, 1);
for (int i = 0; i < lim; i++) {
tmp[i] *= g[i];
}
fft(tmp, -1);
for (int i = 0; i < lim; i++) {
g[i] *= g[i];
}
fft(g, -1);
for (int i = 0; i <= m; i++) {
dp[i] += tmp[i];
dp[i] = int(dp[i].real() + 0.5) % mod;
}
for (int i = m + 1; i < lim; i++) {
g[i] = 0;
}
}
}
int main() {
scanf("%d %d", &m, &mod);
scanf("%d", &n);
scanf("%d", &o);
scanf("%d", &s);
scanf("%d", &u);
for (lim = 1; lim <= 2 * m; lim <<= 1) bit++;
for (int i = 0; i < lim; i++) {
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
for (int i = 1; i <= m; i++) {
f[i] = (o * i * i + s * i + u) % mod;
}
fft(f, 1);
solve(dp, g, n);
printf("%dn", int(dp[m].real() + 0.5));
return 0;
}
最后
以上就是粗心自行车为你收集整理的【JSOI 2012】分零食(倍增 + FFT)的全部内容,希望文章能够帮你解决【JSOI 2012】分零食(倍增 + FFT)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复