0w1

ARC 071 F - Infinite Sequence ( DP )

F: Infinite Sequence - AtCoder Regular Contest 071 | AtCoder

題意:
問有多少無窮長度數列 A[] 滿足:
1. i 的下一個元素開始連續 A[ i ] 個皆相同
2. 對所有的 N ≤ i, j,A[ i ] = A[ j ]
輸出答案對 1e9 + 7 取模。

制約:
1≤n≤1e6
n は整数

解法:
首先可以發現,如果首項是大於 1 的數,那麼,下個數如果也大於 1,就只會有一個方案,因為形如 [ 2, 2 ] 的東西一旦產生就會必須永遠都長這個樣子。因此其實狀態是很少而且很顯然的,考慮枚舉第一項的值,並令 dp[ i ] 為剩餘長度為 i 時的答案:
A.front() = 1 : dp[ i - 1 ]
A.front() = 2 : dp[ i - 3 ] + N - 1
A.front() = 3 : dp[ i - 4 ] + N - 1
.
.
如果下一個數字決定要用 1,就會轉移到子問題的 dp,否則就是 [ 2, N ] 挑個數字數列就唯一確定了。
再考慮一下也可以知道 dp[ x ],對於所有 x ≤ 0,都可以視作 1。
賽中只有想到這裡,然後連範測都無法過。
原因是,dp[ 1 ] 特例是 N,如果不當作邊界條件直接給這個值而是用上面的式子遞推會錯,因為 N i, j,所以後面的數字是唯一確定的。

時間 / 空間複雜度:
O( N )

#include <bits/stdc++.h>
using namespace std;
 
const int MOD = ( int ) 1e9 + 7;
const int MAXN = ( int ) 1e6;
 
int N;
int dp[ MAXN + 1 ];
int pdp[ MAXN + 1 ];
 
signed main(){
  ios::sync_with_stdio( 0 );
  cin >> N;
  pdp[ 0 ] = dp[ 0 ] = 1;
  pdp[ 1 ] = dp[ 1 ] = N;
  ++pdp[ 1 ];
  for( int i = 2; i <= N; ++i ){
    dp[ i ] = ( 1LL * pdp[ i - 1 ] - ( i - 2 >= 0 ? dp[ i - 2 ] : 0 ) + ( N - ( i - 1 ) ) ) % MOD;
    ( dp[ i ] += 1LL * ( N - 1 ) * ( N - 1 ) % MOD ) %= MOD;
    pdp[ i ] = ( pdp[ i - 1 ] + dp[ i ] ) % MOD;
  }
  if( dp[ N ] < 0 ) dp[ N ] += MOD;
  cout << dp[ N ] << endl;
  return 0;
}