【JSOI2012】分零食

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
#include
#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);
}

你可能感兴趣的:(数位dp,动态规划,FFT,NTT,FWT……)