Codeforces 1061C (DP+滾動數組)
阿新 • • 發佈:2018-11-25
這樣的 max span 因數 狀態 mat force inline display
題面
傳送門
分析
考慮DP
設\(dp[i][j]\)表示前i個數選出的序列長度為j的方案數
狀態轉移方程為:
\[ dp[i][j]= \begin{cases}dp\left[ i-1\right] \left[ j-1\right] +dp\left[ i-1\right] \left[ j-1\right] ,j \equiv 0 (\mod i) \\ dp\left[ i-1\right] \left[ j-1\right],otherwise \end{cases} \]
如果二維DP,直接從1~n枚舉i,j,顯然會MLE
發現第一維狀態i只和i-1有關,考慮用類似01背包的方法去掉一維
設dp[j]表示長度為j時的狀態
第i-1次循環結束後,\(dp[j]\)存儲的是原來\(dp[i-1][j]\)的值
然後我們類似01背包,倒序循環j (j為i的因數,若j不是i的因數,dp值不變,不必循環) dp[j]+=dp[j-1]
我們更新dp[j]時,dp[j-1]存的還是原來\(dp[i-1][j-1]\)的值,這樣便不會重復更新
另外,因數個數怎麽解決呢
我們用這樣的篩法,可以在\(O(n\log n)\)的時間內篩出1~n的因數
vector<int>d[maxA]; void sieve(int x){ for(int i=1;i<=x;i++){ for(int j=1;j<=x/i;j++){ d[i*j].push_back(i); } } }
所以總的時間復雜度為\(O(n\log \max(a_i))\)
代碼
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> #define maxn 100005 #define maxA 1000005 #define mod 1000000007 using namespace std; int n; int maxa; int a[maxn]; int dp[maxA]; vector<int>d[maxA]; void sieve(int x){ for(int i=1;i<=x;i++){ for(int j=1;j<=x/i;j++){ d[i*j].push_back(i); } } } int cmp(int x,int y){ return x>y; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); maxa=max(maxa,a[i]); } dp[0]=1; sieve(maxa); for(int i=1;i<=n;i++){ for(int j=d[a[i]].size()-1;j>=0;j--){ int x=d[a[i]][j]; dp[x]+=dp[x-1]; dp[x]%=mod; } } long long ans=0; for(int i=1;i<=n;i++){ ans+=dp[i]; } ans%=mod; cout<<ans; }
Codeforces 1061C (DP+滾動數組)