嘘~ 正在从服务器偷取页面 . . .

洛谷P5488 差分与前缀和 题解


洛谷P5488 差分与前缀和 题解

题目链接:P5488 差分与前缀和

题意

给定一个长为 $n$ 的序列 $a$,求出其 $k$ 阶差分或前缀和。

结果的每一项都需要对 $1004535809$​ 取模。

输入格式

第一行三个整数 $n,k,t$,若 $t=0$ 表示求前缀和,$t=1$ 表示求差分。

第二行 $n$ 个整数,表示序列 $a$​。

输出格式

输出一行 $n$ 个整数,表示 $a$ 的 $k$ 阶差分或前缀和。

数据范围

$1 \le n \le 10^5,~0 \le a_i \le 10^9,~1\le k \le 10^{2333}, k \not \equiv 0 \pmod{1004535809}$​​

$\mathcal{Part}\ 1$

设 $F(x) = \sum_{n \ge 0} a_nx^n$ 是序列 $a$ 的生成函数。

众所周知,它的前缀和就是 $F(x)\times G(x)$ ,其中 $G(x)=\sum_{n\ge 0}x^n$ ,因为

可以发现后面的 $\sum_{i=0}^n a_i$ 就是我们要的前缀和。

因为 $G(x)=\frac{1}{(1-x)}$ ,并且卷积是有结合律的,所以做 $k$ 次前缀和就是

又因为

所以这个东西是可以递推的。那么只需要一个 NTT 就可以搞定了


$\mathcal{Part}\ 2$

差分也是一样的,不过我也不晓得差分的 $G(x)$ 是啥,干脆推一下吧

定义 $a_{-1}=0$ ,则

可以发现,$b_0=1,\,b_1=1,\,b_{i}=0(i > 2)$ ,所以差分的 $G(x) = 1-x$ ,

那做 $k$ 次就是 $G^k(x) = (1-x)^k$ ,如下

这里的递推式是这样的

那么这道题就做完了


时间复杂度 $\mathcal{O}(n \log n)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int P = 1004535809;
const int G = 3, Gi = 334845270;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(4e5 + 15)) // 一定要开 len 的两倍!!!

int qpow(int a, int b)
{
    int r = 1;
    for(; b; b >>= 1, a = a * a % P)
        if(b & 1) r = r * a % P;
    return r;
}
char K[N];
int l, limit, r[N], a[N], b[N];
void init(int len)
{
    for(limit = 1, l = 0; limit <= len; limit *= 2, ++l);
    for(int i = 0; i < limit; i++)
        r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
}
void NTT(int *A, int type)
{
    for(int i = 0; i < limit; i++) if(i < r[i]) swap(A[i], A[r[i]]);
    for(int mid = 1; mid < limit; mid *= 2)
    {
        int Wn = qpow((type == 1 ? G : Gi), (P - 1) / (mid * 2));
        for(int j = 0; j < limit; j += (mid * 2))
        {
            int w = 1;
            for(int k = 0; k < mid; k++, w = (w * Wn) % P)
            {
                int x = A[j + k], y = w * A[j + k + mid] % P;
                A[j + k] = (x + y) % P; A[j + k + mid] = (x - y + P) % P;
            }
        }
    }
    if(type != 1) {
        const int Inv = qpow(limit, P - 2);
        for(int i = 0; i < limit; i++) A[i] = A[i] * Inv % P;
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n, type, k = 0; cin >> n >> (K + 1) >> type; b[0] = 1; --n;
    for(int i = 1; K[i]; i++) k = (k * 10 + (K[i] - '0')) % P;
    for(int i = 0; i <= n; i++) cin >> a[i], a[i] = (a[i] + P) % P;
    if(type == 0) { 
        for(int i = 1; i <= n + 1; i++)
            b[i] = b[i - 1] * (k + i - 1) % P * qpow(i, P - 2) % P;
    }else {
        for(int i = 1; i <= n + 1; i++)
            b[i] = (P - b[i - 1] * (k - i + 1 + P) % P * qpow(i, P - 2) % P) % P;
    }
    init(n + n + 1); NTT(a, 1); NTT(b, 1);
    for(int i = 0; i < limit; i++) a[i] = a[i] * b[i] % P;
    NTT(a, -1); for(int i = 0; i <= n; i++) cout << a[i] << " \n"[i == n];
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/4gtx4ylr

[2] https://www.luogu.com.cn/article/7c1f6n3k


题外话

继续放图片


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录