E - Count Arithmetic Subsequences
キーワード
解説
に対して、 を次のように定義する。
初項 、第二項 、長さ の等差数列の個数
のときは、 初項と第二項によって等差数列が一意に定まるので、 である。
のとき、初項 、第二項 、長さ の等差数列の個数は、 を満たすすべての に対して、初項 、第二項 、長さ の等差数列の個数を足し合わせたものである。
よって、 に対して、次の漸化式が成り立つ。
この漸化式を用いて、 を求める。 の順に求めることで、 を求めることができる。
最終的な答えは、各 について、 の総和である。長さ の等差数列は 個あるので、 のときは である。
の三次元 DP で、遷移の際に の計算が必要なので、全体の時間計算量は である。
の解法もあるが、そちらは公式解説に任せる。
提出コード
- Go
func main() {
n := readInt()
A := readInts()
dp := make([][][]int, n+1)
for i := 1; i <= n; i++ {
dp[i] = make([][]int, n+1)
for j := 1; j <= n; j++ {
dp[i][j] = make([]int, n+1)
if n > 1 {
dp[i][j][2] = 1
}
}
}
for i := n; i >= 1; i-- {
for j := i + 1; j <= n; j++ {
for l := 3; l <= n; l++ {
for k := j + 1; k <= n; k++ {
if A[j-1]-A[i-1] != A[k-1]-A[j-1] {
continue
}
dp[i][j][l] += dp[j][k][l-1]
dp[i][j][l] %= MOD
}
}
}
}
Ans := make([]int, n+1)
Ans[1] = n
for l := 2; l <= n; l++ {
for i := 1; i <= n; i++ {
for j := i + 1; j <= n; j++ {
Ans[l] += dp[i][j][l]
Ans[l] %= MOD
}
}
}
for i := 1; i <= n; i++ {
write(Ans[i], " ")
}
writeLine()
}
- C++
#define MOD 998244353;
int main() {
int n; cin >> n;
vector<ll> A(n); rep(i,0,n) cin >> A[i];
vector<vector<vector<ll>>> dp(n+1,vector<vector<ll>>(n+1,vector<ll>(n+1,0)));
if (n > 1) rep(i,1,n+1) rep(j,i+1,n+1) dp[i][j][2] = 1;
rrep(i,n,1) rep(j,i+1,n+1) rep(l,3,n+1) rep(k,j+1,n+1) {
if (A[j-1]-A[i-1] != A[k-1]-A[j-1]) continue;
dp[i][j][l] += dp[j][k][l-1];
dp[i][j][l] %= MOD;
}
vector<ll> Ans(n+1,0);
Ans[1] = n;
rep(l,2,n+1) rep(i,1,n+1) rep(j,i+1,n+1) {
Ans[l] += dp[i][j][l];
Ans[l] %= MOD;
}
rep(i,1,n+1) cout << Ans[i] << " "; cout << endl;
}