競技プログラミング
主客転倒について
公開日:
2021/05/07
主客転倒について

※本稿を執筆しているのは競プロ初心者であるため、読みづらかったり間違いを含んでいる可能性があります。ご了承下さい。またABCの過去問のネタバレを含んでいます。

競プロの問題の典型に、盤面に対してコストが設定されるときあらゆる盤面に対してコストを計算しその和を出力せよ、というものがある。例えば以下のような問題。

例題

長さNNの数列A=(a1,a2,...,an)A=(a_1,a_2,...,a_n)が与えられる。うぇかりー君は数列から連続した部分列を選ぶのが好きであり、数列AAからal,al+1,...,ara_l,a_{l+1},...,a_rを選んだときうぇかりー君の喜び度はi=lrai\sum_{i=l}^r a_iと定義される。数列AAのあらゆる連続した部分列の選び方に対してうぇかりー君の喜び度を計算しその和を109+710^9+7で割った余りを出力せよ。

制約

1N105,1ai1051{\leq}N{\leq}10^5,1{\leq}a_i{\leq}10^5

例題に対する入力としてN=3,A=(1,2,3)N=3,A=(1,2,3)が与えられたとすると、AAに対する連続した部分列の選び方は(1,2,3)(1,2,3)()()(何も選ばない)、(1)(1)(2)(2)(3)(3)(1,2)(1,2)(2,3)(2,3)で全てであり、答えは6+0+1+2+3+3+5=206+0+1+2+3+3+5=20となる。
長さに関する制約から選び方をすべて列挙して計算するのはO(N2)O(N^2)かかって厳しい。なので、かわりにaia_iを含む連続する部分列の選び方が何通りあるかを数える。aia_iが含まれる連続する部分列の個数はi×(Ni+1)i\times{(N-i+1)}個であるので、答えのコストのうちaia_iが選ばれることによるコストはi×(Ni+1)×aii\times{(N-i+1)}\times{a_i}となる。よって例題の答えは以下のコードで計算出来る。(入力は省略)

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

この記事を書いた人
motu84834のプロフィール画像
motu84834
(2018年入学)
loading...