F - Range Power Sum Editorial by drogskol

exp

k乗和と exp

一般に,k乗和を見たら \(e^{ax}\) の総積を考えると上手くいくことがあります.
これは以下の性質が成り立つからです.

  • \(e^{ax} e^{bx} = e^{(a+b)x}\)(性質1)
  • \(k![x^k]e^{ax} = a^k\)(性質2)

ただし,多項式 \(f(x)\) の k 次の係数を \([x^k]f(x)\) で表しています.

解法

今回の問題は

\[\sum_{l,r}(\sum_{i\in[l,r]} A_i)^k\]

ですが,これは難しいので代わりにk乗和を総積に置き換えた以下のような問題を考えてみます.

\[\sum_{l,r}\prod_{i\in[l,r]} f_i\]

これは各 \(r\) について \(\sum_{l}\prod_{i\in[l,r]} f_i\) を足し合わせればよく,以下のようなコードで線形時間で解くことが出来ます.

def solve(f: list[int]) -> int:
    result = 0
    sum = 0
    for x in f:
        sum += 1
        sum *= x
        result += sum
    return result

ここで \(f_i \coloneqq e^{A_ix}\) を代入してみると(性質1)より

\[ \sum_{l,r}\prod_{i\in[l,r]} f_i = \sum_{l,r}\prod_{i\in[l,r]} e^{A_ix} = \sum_{l,r}e^{\sum_{i\in[l,r]} A_ix} \]

が成り立ちます.
したがって(性質2)より \(k![x^k]\sum_{l,r}\prod_{i\in[l,r]} f_i\) が今回の問題の答えです.

ここで FPS は全て \(k\) 次の項まで持てば充分であるため,足し算は \(\Theta(k)\),掛け算は \(\Theta(k^2)\) 時間で行えることに留意すると,全体で \(\Theta(nk^2)\) 時間程度でこの問題を解くことが出来ます.

実装例

#include <atcoder/all>
#include <bits/stdc++.h>

using mint = atcoder::modint998244353;
using poly = std::vector<mint>;

int k;
poly poly_exp(int a) {
    poly res(k + 1, 1);
    for (int i = 1; i <= k; i++)
        res[i] = res[i - 1] * mint::raw(a) / mint::raw(i);
    return res;
}
void multiply(poly &f, const poly &g) {
    f = atcoder::convolution(f, g);
    f.resize(k + 1);
}

int main() {
    int n;
    std::cin >> n >> k;

    mint ans = 0;
    poly sum(k + 1, 0);

    while (n--) {
        int a;
        std::cin >> a;
        sum[0]++;
        multiply(sum, poly_exp(a));
        ans += sum[k];
    }

    for (int i = 1; i <= k; i++)
        ans *= i;

    std::cout << ans.val() << std::endl;
}

似たような話 https://noshi91.hatenablog.com/entry/2022/12/31/015729

posted:
last update: