「BZOJ4332」「JSOI2012」分零食

本文探讨了一个有趣的数学问题:如何计算在特定条件下分配糖果给幼儿园小朋友时,所有小朋友欢乐程度乘积的期望值。通过动态规划和快速傅里叶变换等算法解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里是欢乐的进香河,这里是欢乐的幼儿园。
今天是 2214 日,星期二。在这个特殊的日子里,老师带着同学们欢乐地跳着,笑着。校长从幼儿园旁边的小吃店买了大量的零食决定分给同学们。听到这个消息,所有同学都安安静静地排好了队,大家都知道,校长不喜欢调皮的孩子。
同学们依次排成了一列,其中有 AA 位小朋友,有三个共同的欢乐系数 O,SUU 。如果有一位小朋友得到了 x 个糖果,那么她的欢乐程度就是 f(x)=Ox2+Sx+Uf(x)=O∗x2+S∗x+U
现在校长开始分糖果了,一共有 MM 个糖果。有些小朋友可能得不到糖果,对于那些得不到糖果的小朋友来说,欢乐程度就是 1 。如果一位小朋友得不到糖果,那么在她身后的小朋友们也都得不到糖果。(即这一列得不到糖果的小朋友一定是最后的连续若干位)
所有分糖果的方案都是等概率的。现在问题是:期望情况下,所有小朋友的欢乐程度的乘积是多少?呆呆同学很快就有了一个思路,只要知道总的方案个数T和所有方案下欢乐程度乘积的总和 SS ,就可以得到答案 Ans=ST 。现在他已经求出来了 TT 的答案,但是 S 怎么求呢?他就不知道了。你能告诉他么?
因为答案很大,你只需要告诉他 SSP 取模后的结果。
后记:
虽然大家都知道,即便知道了 TT ,知道了 SPP 取模后的结果,也没有办法知道期望情况下,所有小朋友欢乐程度的乘积。但是,当呆呆想到这一点的时候,已经彻底绝望了。

Input

第一行有 2 个整数,分别是 MMP
第二行有一个整数 AA ,第三行有一个整数 O
第四行有一个整数 SS ,第五行有一个整数 U

Output

一个整数 SS ,因为答案可能很大,你只需要输出 SPP 取模后的结果。

Sample Input

4 100 
4 
1 
0 
0 

Sample Output

63 

样例说明

函数 f(x)=x2 。一共有 44 份零食,4 位同学。如果只有第一个同学得到,欢乐程度为 1616 ,若前两位同学得到,欢乐程度的所有可能依次为 9,9,169,9,16,若有三位同学得到,欢乐程度有 4,4,44,4,4,最后一种情况,每一个同学都得到了零食,欢乐程度为 11 。相加后得到 S=63

HINT

对于 100%100% 的数据,M10000,P255,A108,O4,S300,U100M≤10000,P≤255,A≤108,O≤4,S≤300,U≤100

题解

首先想到 dp 。g[i][j]g[i][j] 表示前 ii 个小朋友分到 j 块糖的所有方案 SS 之和,然后答案是 i=1ng[i][m]
dp方程

g[n][m]=i=1mg[n1][i]×f(mi)g[n][m]=∑i=1mg[n−1][i]×f(m−i)
(枚举第 nn 个小朋友分到的糖数)。
然后发现是个卷积的形式,于是立刻想到 FFT ,立刻想到倍增 (
g[n]=g[n1]f
得到
g[n]=g[0]fng[n]=g[0]∗fn
,而 g[0]=1g[0]=1 所以
g[n]=fng[n]=fn
)。但是我们显然要求的是
i=1ng[i][m]∑i=1ng[i][m]
只是这样倍增显然是不行的。

于是记

F[n]=i=1ng[i]F[n]=∑i=1ng[i]

F[n][m]F[n][m] 即为答案。
我们还是可以用倍增的方式求 F[n]F[n] (以下设 nn2 的倍数)。

F[n]=i=1ng[i]F[n]=∑i=1ng[i]

F[n]=F[n2]+i=n2+1ng[i]F[n]=F[n2]+∑i=n2+1ng[i]

F[n]=F[n2]+i=n2+1nfiF[n]=F[n2]+∑i=n2+1nfi

F[n]=F[n2]+i=1n2fi+n2F[n]=F[n2]+∑i=1n2fi+n2

F[n]=F[n2]+fn2i=1n2fiF[n]=F[n2]+fn2∑i=1n2fi

F[n]=F[n2]+g[n2]F[n2]F[n]=F[n2]+g[n2]∗F[n2]

完成!

对于 n mod 2=1n mod 2=1 的情况,可以从 F[n1]F[n−1] 来计算 F[n]F[n]。可以证明,迭代次数是 log2nlog2n 级别的。
于是就可以开心的使用倍增完成,取膜可以在求完卷积时膜。

OSU是坠吼的

复杂度 O(nlog2nlog2m)O(nlog2⁡nlog2⁡m)

My Code

/**************************************************************
    Problem: 4332
    User: infinityedge
    Language: C++
    Result: Accepted
    Time:3868 ms
    Memory:4364 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <complex>

#define MAXN 32768
#define pi acos(-1)

using namespace std;

typedef long long ll;
typedef complex<double> E;

int p;

void bit_reverse(int n, E* r){
    for(int i = 0, j = 0; i < n; i ++){
        if(i > j) swap(r[i], r[j]);
        for(int l = n >> 1; (j ^= l) < l; l >>= 1);
    }
}

void fft(int n, E* r, int f){
    bit_reverse(n, r);
    for(int i = 2; i <= n; i <<= 1){
        int m = i >> 1;
        for(int j = 0; j < n; j += i){
            E w(1, 0), wn(cos(2 * pi / i), f * sin(2 * pi / i));
            for(int k = 0; k < m; k ++){
                E z = r[j + m + k] * w;
                r[j + m + k] = r[j + k] - z;
                r[j + k] = r[j + k] + z;
                w = w * wn;
            }
        }
    }
    if(f == -1){
        for(int i = 0; i < n; i ++) r[i] /= n, r[i] = int(r[i].real() + 0.1) % p;
    }
}

int m, n, o, s, u, N;

E F[MAXN], tmp[MAXN]; 

void solve(E* f, E* g, int n){
    if(n == 0){
        g[0] = 1; return;
    }
    if(n % 2 == 1){
        solve(f, g, n - 1);
        fft(N, g, 1);
        for(int i = 0; i < N; i ++) g[i] = g[i] * F[i];
        fft(N, g, -1);
        for(int i = 0; i <= m; i ++) f[i] = f[i] + g[i], f[i] = int(f[i].real() + 0.1) % p;
        for(int i = m + 1; i < N; i ++) g[i] = 0;
    }else{
        solve(f, g, n / 2);
        for(int i = 0; i < N; i ++) tmp[i] = f[i];
        fft(N, tmp, 1);
        fft(N, g, 1);
        for(int i = 0; i < N; i ++) tmp[i] = tmp[i] * g[i];
        fft(N, tmp, -1);
        for(int i = 0; i < N; i ++) g[i] = g[i] * g[i];
        fft(N, g, -1);
        for(int i = 0; i <= m; i ++) f[i] = f[i] + tmp[i], f[i] = int(f[i].real() + 0.1) % p;
        for(int i = m + 1; i < N; i ++) g[i] = 0;
    }
}

E a[MAXN], b[MAXN];

E g[MAXN], f[MAXN];


int main(){
    scanf("%d%d%d%d%d%d", &m, &p, &n, &o, &s, &u);
    N = 1; while(N < 2 * m + 1) N = N << 1;
    for(int i = 1; i <= m; i ++){
        F[i] = (o * i * i + s * i + u) % p;
    }
    fft(N, F, 1);
    solve(f, g, n);
    printf("%d\n", int(f[m].real() + 0.1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值