1. 程式人生 > 其它 >AtCoder Beginner Contest 207 E

AtCoder Beginner Contest 207 E

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}\)

表示當前列舉到第 \(i\) 位且到第 \(i\) 位為止是第 \(j\) 個數列。那麼這樣的一個轉移式需要我們去列舉 \(i,j,k\),顯然,這麼做的時間複雜度是 \(O(n^3)\)。我們承受不了這麼大的時間複雜度,考慮優化。

​ 注意到當 \((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;
}