1. 程式人生 > >Codeforces 1061C (DP+滾動數組)

Codeforces 1061C (DP+滾動數組)

這樣的 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+滾動數組)