【AtCoder】ABC 405 C - Sum of Product | 緑コーダーが解くAtCoder
AtCoder-ABC-ABC_C問題
AtCoder-ABC-ABC_300点問題
AtCoder-NoviSteps-NoviSteps_4Q
AtCoder-累積和-累積和テーブルを陽に構築しない
AtCoder-数学-式変形
AtCoder-ABC
AtCoder-NoviSteps
AtCoder-AtCoder_Diff不明
AtCoder-二重シグマの高速化
AtCoder-累積和
AtCoder-数学
AtCoder
ABC
ABC_C問題
ABC_300点問題
AtCoder_Diff不明
NoviSteps_4Q
二重シグマの高速化
累積和テーブルを陽に構築しない
式変形
競技プログラミング
C++
配点: 300 点 / 実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: ??? / NoviSteps: 4Q
問題概要
長さ の整数列
が与えられるので、
の値を求めよ。
制約
- 入力は全て整数。
考察
二重シグマを愚直に計算していたのでは、 の計算量となってしまい TLE する。
これを避けるためには、どうにかして計算量を改善しなければならない。様々な考え方ができるが、ここでは汎用的な2つの例を書くことにする。
累積和を用いる
として考えてみると、求める答えは以下のように工夫して計算できる。
括弧でくくった部分は累積和の形となり、この部分は定数時間で計算できるようになる。
を一般の場合に変えた場合のアルゴリズムは以下の通り。
- 答え用の変数
と、累積和用の変数
をそれぞれ
で初期化する。
について、以下の処理を行う。
なお、今回は累積和テーブルの区間和が1個ずつ変更されないので、「前処理 で累積和テーブルを構築する」ようなことはしていない。
オーバーフローに注意すること。
式変形から導く
高校数学では、以下のような変形をよく用いたと思う。
これを一般化すると、以下のようになる(証明は割愛するが、帰納法で可能だと思う)。
あとは、これをオーバーフローに注意しつつ実装すればよい。
実装例
以下のコードは 0-indexed である。
考察1
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) using ll = long long; // ======================================== // int main() { int N; cin >> N; vector<ll> A(N); rep(i, 0, N) cin >> A[i]; ll ans = 0, sum = A[0]; rep(i, 1, N) { ans += A[i] * sum; sum += A[i]; } cout << ans << endl; return 0; }
考察2
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) using ll = long long; // ======================================== // int main() { int N; cin >> N; vector<ll> A(N); rep(i, 0, N) cin >> A[i]; ll sum = 0, sq_sum = 0; rep(i, 0, N) { sum += A[i]; sq_sum += A[i] * A[i]; } ll ans = (sum * sum - sq_sum) / 2; cout << ans << endl; return 0; }
実装時間: 5分以内
コメント
この問題とほぼ同じ問題が、 ABC177-C で出題されている。
知っていれば瞬殺できるので、二重シグマ系の問題は一通りまとめておきたい。
今回の類題パターンは以下の記事にまとまっていたので紹介する。