ABC177に参加してみた

初めに

ABC177はAB完でした。C問題はほぼほぼ解けていたんですが
問題文を読み間違えている事に最後まで気づかず回答できませんでしたorz
はい、ということで今回はC問題について書いていきます。。。

https://atcoder.jp/contests/abc177

※ブログ内で問題の制約事項をすべて記載はしないので、制約事項は↑のURLより参照願います。

C - Sum of product of pairs

N個の整数A_1 ~ A_Nが与えられるので1 \le i \lt j \le Nを満たす
すべての(i,j)についてのA_i × A_jの和をmod(10^9+7)で割った数を求める問題です。

解説を見てからの回答
#include<bits/stdc++.h>

using namespace std;
using ll = long long;
const long long INF = 1LL<<60;

typedef pair<ll,ll> Pair;

int main(){

  ll N;
  ll ans = 0;
  ll mod = 1e9 + 7;
  cin >> N;

  vector<ll> A(N);
  vector<ll> S(N + 1);

  for (ll i = 0; i < N;++i)
    cin >> A[i];

  S[0] = 0;

  for (ll i = 0; i <N;++i){

    // 累積和を求める
    S[i+1] = (S[i] + A[i]);

  }

  for (ll i = 0; i < N;++i){
    // 区間和を求める
    ll sum = (S[N] - S[i + 1]) % mod;
    // AiAjの計算を行い変数ansに加算し、mod計算を行う
    ans += sum*A[i];
    ans %= mod;
  }

  cout << ans << endl;

  return 0;

}

この問題は愚直に2重ループで計算を行っていくとNの制約が2 \le N \le 2×10^5のため、
制限時間内に計算を行う事が出来ません。

まずは何か法則性が無いか考えてみましょう。N = 4の場合を考えて見ます。
A_iA_0,A_1,A_2の場合の計算は以下のようにまとめる事が可能です。
※0基算で書いてます。

f:id:hgm_blog:20200830164411p:plain
イメージ

上記よりA_iA_i以降の値を足した数を掛け算し、それぞれのiでの計算結果を
足し合わせることでも答えが得られそうです。

2重ループで足し算をした後に掛け算を行うと制限時間内に間に合わないので
下記のように累積和をまずは求めます。累積和を求めておけばある区間の和も
O(1)で求める事が出来ます。

f:id:hgm_blog:20200830182045p:plain
イメージ

あとは、A_i区間和を掛けていきその和を求めていきます。
区間和およびA_i区間和の積を求める際はmod計算も忘れずに行いましょう。
(オーバーフローの可能性があるため。mod計算は計算途中に行っても計算結果は変わりません。)

最後に計算結果を出力すれば問題を解くことが出来ます。

最後に

うーん今回はもったいなかったですね。ほとんど解けていたようなものだったというのと
割と回答内容に自身があった分、WAが発生した時に余計混乱してしまいました。。。
落ち着いて取り組みましょ!

※累積和の計算段階からmod計算をしていたので、C問題はWAになってしまいました。。。