我是靠谱客的博主 粗心自行车,最近开发中收集的这篇文章主要介绍【JSOI 2012】分零食(倍增 + FFT),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题目链接:【JSOI 2012】分零食

题目大意:将 m m 颗糖依次分给 n 个小朋友,每个小朋友的快乐度为 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 个小朋友得到 j 颗糖的答案。则: g[i][j]=jk=1f(jk)g[i1][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]+fnni=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)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部