「BZOJ4332」「JSOI2012」分零食

Description

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

Input

第一行有 2 个整数,分别是 M P
第二行有一个整数 A ,第三行有一个整数 O
第四行有一个整数 S ,第五行有一个整数 U

Output

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

Sample Input

4 100 
4 
1 
0 
0 

Sample Output

63 

样例说明

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

HINT

对于 100 % 的数据, M 10000 , P 255 , A 108 , O 4 , S 300 , U 100

题解

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

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

于是记

F [ n ] = i = 1 n g [ i ]

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

F [ n ] = i = 1 n g [ i ]

F [ n ] = F [ n 2 ] + i = n 2 + 1 n g [ i ]

F [ n ] = F [ n 2 ] + i = n 2 + 1 n f i

F [ n ] = F [ n 2 ] + i = 1 n 2 f i + n 2

F [ n ] = F [ n 2 ] + f n 2 i = 1 n 2 f i

F [ n ] = F [ n 2 ] + g [ n 2 ] F [ n 2 ]

完成!

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

OSU是坠吼的

复杂度 O ( n log 2 n log 2 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;
}

猜你喜欢

转载自blog.csdn.net/infinity_edge/article/details/78777089