問題
https://codeforces.com/contest/1861/problem/E (diff 2300)
以上以下の整数からなる長さの数列について、の美しさを以下の通り定める。
- からからの値が丁度一回ずつ出現するの連続部分列を、共通区間が存在しないように個取り出せるとする
- としてありうる最大値
以上以下の整数からなる長さの数列全ての美しさの総和を求めよ。
制約
解法
美しさの求め方を考える。尺取り法でからの値が丁度一回出現するの区間を列挙できる。したがって区間スケジューリング問題に帰着する。
今回は全ての区間の長さがで等しいので、左端がより前のものから貪欲に取るのが正当になる。 (*)
この事実から数え上げを頑張る。
前から項目までを考慮した、「現在区間として取っていない」かつ同じ要素が出現しないsuffixの最大長がであるような数列の数
とする。(*)から、のみを考慮すれば良い。なぜなら、同じ要素が出現しないsuffixの最大長が、すなわちのときその区間はすでに取っているからである。
dpの初期値:
dpの遷移
末尾項に登場した値を追加するとき
末尾の値と同じ値を追加するとに配ることになる
末尾の一個前の値と同じ値を追加するとに配ることになる
末尾の二個前の値と同じ値を追加するとに配ることになる
末尾の個前の値と同じ値を追加するとに配ることになる
以上をもらうdpで考え直すと、累積和で高速化できるかたちになる
末尾項に登場しない値を追加するとき
そのような値の候補は個ある。なのでにを配ることになる。
でかつ最後登場していない値を追加するとき、その区間を「とる」ことになるので、遷移先は
解の計算
dpの計算で区間を取るとき、すなわち「でかつ最後登場していない値を追加するとき」、残り項を自由に決めた数列全てがその区間で美しさに寄与するので、を解に足してあげる
以上より、で本問題を解くことができた。
実装
#include <bits/stdc++.h> #include "Src/Template/IOSetting.hpp" using namespace zawa; #include "atcoder/modint" using mint = atcoder::modint998244353; int main() { SetFastIO(); int n, k; std::cin >> n >> k; std::vector<mint> dp(k); dp[0] = mint::raw(1); mint ans{}; std::vector<mint> P(n + 1); P[0] = mint::raw(1); for (int i{1} ; i <= n ; i++) P[i] = P[i - 1] * mint{k}; for (int i{} ; i < n ; i++) { std::vector<mint> sum(k + 1); for (int j{} ; j < k ; j++) sum[j + 1] += sum[j] + dp[j]; std::vector<mint> next(k); for (int j{} ; j < k ; j++) { if (j == k - 1) { ans += dp[j] * P[n - i - 1]; next[0] += dp[j]; } if (j < k - 1) { next[j + 1] += mint::raw(k - j) * dp[j]; } // for (int l{1} ; l <= j ; l++) { // next[l] += dp[j]; // } } for (int j{1} ; j < k ; j++) { next[j] += sum[k] - sum[j]; } dp = std::move(next); } std::cout << ans.val() << '\n'; }
感想
- 沢山時間をかけたが、自力で解くことができた。やった。
- ARC-CやAGC-A,Bの数え上げ問題を毎回解けずにいるので、今回自力で解けて手応えを感じた。
- Fancy Arraysも然り、ECRの数え上げは綺麗な解法が多いと思う