【JSOI2012】分零食

本文探讨了一个有趣的数学问题:如何在幼儿园小朋友中公平分配有限数量的糖果,以最大化他们的整体快乐指数。通过使用数位DP和FFT算法,文章提供了一种有效的解决方案。

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

Description:

这里是欢乐的进香河,这里是欢乐的幼儿园。

今天是 2 月 14 日,星期二。在这个特殊的日子里,老师带着同学们欢乐地跳着,笑着。校长从幼儿园旁边的小吃店买了大量的零食决定分给同学们。听到这个消息,所有同学都安安静静地排好了队,大家都知道,校长最不喜欢调皮的孩子。 同学们依次排成了一列,其中有 A位小朋友,有三个共同的欢乐系数 O,S 和 U。如果有一位小朋友得到了 x 个糖果,那么他的欢乐程度就是 f(x)=O*x2+S*x+U。

现在校长开始分糖果了,一共有 M个糖果。有些小朋友可能得不到糖果,对于那些得不到糖果的小朋友来说,欢乐程度就是 1。如果一位小朋友得不到糖果,那么在他的身后的小朋友也都得不到糖果。 (即这一列得不到糖果的小朋友一定是最后的连续若干位) 所有分糖果的方案都是等概率的。现在的问题是:期望情况下,所有小朋友的欢乐程度的乘积是多少?

呆呆同学很快就有了一个思路,只要知道总的方案 T 和所有方案下欢乐程度乘积的总和 S,就可以得到答案 Ans=S/T。现在他已经求出来了 T的答案,但是 S 怎么求呢?他就不知道了,你能告诉他么?

因为答案很大,你只需要告诉他 S对 P 取模后的结果。

后记:

虽然大家都知道,即便知道了 T,知道了 S 对 P 取模后的结果,也没有办法知道期望情况下,所以小朋友欢乐程度的乘积。但是,当呆呆想到这一点的时候,已经彻底绝望了。

M<=10000,P<=255,A<=10^8,O<=4,S<=300,U<=100。

题解:

fi,j表示有i个人,吃了j个零食的乘积和。

那么f1,i=oi2+si+u

如果把fi看作一个多项式,那么fi=(f1)i

答案要求ni=1fi,m

可以用数位dp+FFT来做。

数位dp的话既可以从高位到低位,也可以从低位到高位,维护几个东西,这个是最经典的数位dp。

但是因为好久没写了,写错了。

注意p非常小,所以可以直接NTT。

Code:

#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define ff(i, x, y) for(int i = x; i < y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int mo = 998244353, N = 4e5 + 5;

ll n, m, p, A, o, s, u;
ll s1[N], s0[N], s2[N], re[N], g[N], li[N];

ll ksm(ll x, ll y) {
    ll s = 1;
    for(; y; y /= 2, x = x * x % mo)
        if(y & 1) s = s * x % mo;
    return s;
}
ll w[N], tx;
void dft(ll *a) {
    ff(i, 0, n) {
        int p = i, q = 0;
        fo(j, 1, tx) q = q * 2 + p % 2, p /= 2;
        if(q > i) swap(a[q], a[i]);
    }
    for(int m = 2; m <= n; m *= 2) {
        int h = m / 2;
        ff(i, 0, h) {
            ll W = w[i * (n / m)];
            for(int j = i; j < n; j += m) {
                int k = j + h;
                ll u = a[j], v = a[k] * W % mo;
                a[j] = (u + v) % mo, a[k] = (u - v + mo) % mo;
            }
        }
    }
}
void fft(ll *a, ll *b) {
    ll v = ksm(3, (mo - 1) / n);
    w[0] = 1; fo(i, 1, n) w[i] = w[i - 1] * v % mo;
    dft(a); dft(b);
    ff(i, 0, n) a[i] = a[i] * b[i] % mo;
    fo(i, 0, n / 2) swap(w[i], w[n - i]);
    dft(a); ll ni = ksm(n, mo - 2);
    ff(i, 0, n) a[i] = a[i] * ni % mo;
}

int main() {
    scanf("%lld %lld %lld %lld %lld %lld", &m, &p, &A, &o, &s, &u);
    fo(i, 1, m) g[i] = ((ll) i * i * o + (ll) i * s + u) % p;
    A = min(A, m);
    tx = 0; while(1 << tx ++ <= m); n = 1 << tx;
    s1[0] = 1;
    for(; A; A /= 2) {
        if(A & 1) {
            ff(i, 0, n) re[i] = g[i];
            re[0] ++; fft(s0, re);
            ff(i, 0, n) if(i > m) s0[i] = 0; else s0[i] %= p;

            ff(i, 0, n) s0[i] = (s0[i] + s1[i] + s2[i]) % p;

            ff(i, 0, n) re[i] = g[i]; fft(s1, re);
            ff(i, 0, n) if(i > m) s1[i] = 0; else s1[i] %= p;

            ff(i, 0, n) re[i] = g[i]; fft(s2, re);
            ff(i, 0, n) if(i > m) s2[i] = 0; else s2[i] %= p;
        } else {
            ff(i, 0, n) li[i] = s2[i];
            ff(i, 0, n) s2[i] = (s0[i] + s1[i] + s2[i]) % p;
            ff(i, 0, n) re[i] = g[i];
            fft(s2, re);
            ff(i, 0, n) if(i > m) s2[i] = 0; else s2[i] = (s2[i] + li[i]) % p;
        }
        ff(i, 0, n) re[i] = g[i];
        fft(g, re);
        ff(i, 0, n) if(i > m) g[i] = 0; else g[i] %= p;
    }
    printf("%lld", (s0[m] + s1[m]) % p);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值