※本稿を執筆しているのは競プロ初心者であるため、読みづらかったり間違いを含んでいる可能性があります。ご了承下さい。またABCの過去問のネタバレを含んでいます。
競プロの問題の典型に、盤面に対してコストが設定されるときあらゆる盤面に対してコストを計算しその和を出力せよ、というものがある。例えば以下のような問題。
例題
長さNの数列A=(a1,a2,...,an)が与えられる。うぇかりー君は数列から連続した部分列を選ぶのが好きであり、数列Aからal,al+1,...,arを選んだときうぇかりー君の喜び度は∑i=lraiと定義される。数列Aのあらゆる連続した部分列の選び方に対してうぇかりー君の喜び度を計算しその和を109+7で割った余りを出力せよ。
制約
1≤N≤105,1≤ai≤105
例題に対する入力としてN=3,A=(1,2,3)が与えられたとすると、Aに対する連続した部分列の選び方は(1,2,3)、()(何も選ばない)、(1)、(2)、(3)、(1,2)、(2,3)で全てであり、答えは6+0+1+2+3+3+5=20となる。
長さに関する制約から選び方をすべて列挙して計算するのはO(N2)かかって厳しい。なので、かわりにaiを含む連続する部分列の選び方が何通りあるかを数える。aiが含まれる連続する部分列の個数はi×(N−i+1)個であるので、答えのコストのうちaiが選ばれることによるコストはi×(N−i+1)×aiとなる。よって例題の答えは以下のコードで計算出来る。(入力は省略)
const int MOD = 1e9+7;
long long ans = 0;
int n;
vector<long long> a(n);
for(int i = 0;i < n;i++){
ans += a[i] * (i+1) % MOD * (n-i) % MOD;
ans %= MOD;
}
cout << ans << endl;
答えに含まれる回数などを数え上げることで計算量をへらすのは典型でABCの過去問にもちょこちょこあったりするので、なんとなく覚えておくといいかもしれません。
類題:ABC058-E ABC127-E