AtCoder Beginner Contest 207 E
阿新 • • 發佈:2021-06-27
AtCoder Beginner Contest 207 E
題意:
給定一個長度為 \(n\) 的數列 \(a\)。 我們將 \(a\) 按順序分成若干個數列 \(b_1,b_2\cdots b_k\),對於每一個數列 \(b_i\),必定滿足 \(b_i\) 中所有數字之和被 \(i\) 整除。求合法分割的總方案,由於答案過大,輸出總方案 \(\bmod \;10^9+7\)。 \((1\le N \le3000,1\le A_i\le10^{15})\)。
很明顯的一個 dp 題,我們可以敲出一個暴力的轉移式:
\[dp_{i,j}=\sum_{k=1}^{i-1}{dp_{k,j-1}}\;((sum_i-sum_k)\bmod j = 0) \] 其中 \(dp_{i,j}\)
注意到當 \((sum_i-sum_k)\bmod j =0\) 時,一定有 \(sum_i \equiv sum_ k\mod j\)。由於 \(j\) 的數值很小,所以我們可以在最外部列舉 \(j\),開一個桶 \(f_i\) 表示滿足 \(sum_k \bmod j =i\) 所有 \(dp_{k,j-1}\) 的值,這樣我們可以 \(O(1)\) 進行轉移。總時間複雜度為 \(O(n^3)\)
程式碼如下:
#include<bits/stdc++.h> #define ll long long using namespace std; const int MAXN = 3e3+5; const int MOD = 1e9+7; int n,dp[MAXN][MAXN],f[MAXN]; ll sum[MAXN],a[MAXN]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lld",&a[i]); for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i]; dp[0][0]=1; for(int k=1;k<=n;++k) { for(int i=0;i<=n;++i) f[i]=0; for(int i=0;i<=n;++i) { dp[i][k]=f[sum[i]%k]; f[sum[i]%k]=(f[sum[i]%k]+dp[i][k-1])%MOD; } } ll ans=0; for(int i=1;i<=n;++i) ans=(ans+dp[n][i])%MOD; printf("%lld",ans); return 0; }