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\で、それ以外は
となっています。
したがって、各ブロック[l, r)に対して
の形を高速に求められれば、最後の1項だけ引くことで
を復元できます。
これはあらかじめ累積和で持っておくことで高速に処理できます。
あとは調和級数によって、計算量は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); }