atcoder-solutionsatcoder-solutions
abc452_e

You WILL Like Sigma Problem の解説

調和級数 by @ohnuma

解説

mod がややこしい最大要因なので、まず (i mod j) の並び方に注目します。
この値を倍率と呼ぶことにします。

j = 1 のとき

倍率はすべて0です。

j = 2 のとき

1 0 1 0 1 0 1 0 ...

j = 3 のとき

1 2 0 1 2 0 1 2 ...

j = 4 のとき

1 2 3 0 1 2 3 0 ...

このように、固定したjに対して倍率は長さjの周期列になります。
各長さjのブロックごとに見ると、最後の要素だけ倍率が0\で、それ以外は

1,2,,j11, 2, \dots, j-1

となっています。

したがって、各ブロック[l, r)に対して

k=lr1(kl+1)Ak\sum_{k=l}^{r-1} (k-l+1) A_k

の形を高速に求められれば、最後の1項だけ引くことで

k=lr1((k+1)modj)Ak\sum_{k=l}^{r-1} ((k+1) \bmod j) A_k

を復元できます。

これはあらかじめ累積和で持っておくことで高速に処理できます。

あとは調和級数によって、計算量はO(N log M)で抑えられることをおおまかに見積もって実装しました。

fn main() { input! { n: usize, m: usize, a: [ModInt998244353; n], b: [ModInt998244353; m] } let (pref1, pref2) = { let mut v1 = vec![]; let mut v2 = vec![]; for i in 0..n { v1.push(a[i] * (i + 1)); v2.push(a[i]); } (PrefixSum1D::new(&v1), PrefixSum1D::new(&v2)) }; let mut ans = ModInt998244353::new(0); for (idx, &num) in b.iter().enumerate() { if idx == 0 { continue; } let mut now = 0; let diff = idx + 1; let mut sum = ModInt998244353::new(0); loop { let next = now + diff; let sum1 = pref1.range_sum(now..min(next, n)); let sum2 = pref2.range_sum(now..min(next, n)) * now; let cur = sum1 - sum2 - if next <= n { a[next - 1] * diff } else { ModInt998244353::new(0) }; sum += cur; if next >= n { break; } now = next; } ans += sum * num; } println!("{}", ans); }

コメント