つちのこの日記

競プロと音ゲーがメインです。

yukicoder contest 420 H 別解

問題: No.2670 Sum of Products of Interval Lengths - yukicoder

公式解説が天才っぽかったので、包除原理を使用したシンプルな解法を説明します。(多分最終的な結論は一緒ですが、導出が異なると考えています。)

数え上げるものは、値が $1$ ずつ増加する際の極大なブロックに分割したときの、各ブロックの長さの積の総和です。

極大なブロック間の仕切りを固定したときの答えを考えると、仕切りの左右の要素の差は $1$ ではないという制約が付きます。この制約は扱いづらいので包除原理で処理します。

仕切りのうち、仕切りの左右の要素の差は $1$ という制約が課された仕切りを青い仕切り、制約のない仕切りを赤い仕切りとして、赤い仕切りの位置を固定します。

赤い仕切り同士で区切られた長さ $n$ の区間にたいして青い仕切りを入れる方法全てに対する(包除原理の $-1$ 倍を含めた)答えへの寄与を $f_n$ とすると、$f_n=n-\sum_{i=1}^{n-1}i \times f_i$ という漸化式が立つので $f$ を $\mathrm{O}(N)$ で列挙できます。

(余談:実は $f$ は $(0,1,1,0,-1,-1,0,1,1,0,-1,-1,\ldots)$ と $6$ 個周期で繰り返していることが、漸化式を解くか実験をすることで分かります)

長さ $n$ の極大なブロックの個数を $g_n = \max(m-n+1,0)$ と置けば、求める答えは、形式的冪級数 $F(x) = \sum_{n=1}^{N} f_ng_nx ^ n$ として$[x ^ N] \frac{1}{1-F(x)}$ です。これは $\mathrm{O}(N\log N)$ で計算できます。

実装(の本質部分)

long long n, m;
array<mint, 6> f{0, -1, -1, 0, 1, 1};

int main() {
    cin >> n >> m;
    fps g(n + 1);
    g[0] = 1;
    for(int i = 1; i <= min(n, m); i++) g[i] = f[i % 6] * (m + 1 - i);
    cout << g.inv()[n] << endl;
}