E.
t
a
g
:
tag:
tag:数组分段方案
动态规划
动态规划前缀和优化
同余
传送门:
题意 :
给定一个数组
a
[
]
a[]
a[],询问有多少种方式,可以将其分为任意段,使得其每段
B
i
B_i
Bi满足
B
i
%
i
=
=
0
B_i\%i==0
Bi%i==0
思路 :
首先这题想到的是 动态规划
状态表示 :
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],表示前
i
i
i个数字,分成
j
j
j段的合法方案
状态转移:
f
[
i
]
[
j
]
=
∑
(
s
u
m
i
−
s
u
m
k
)
%
j
=
=
0
f
[
k
]
[
j
−
1
]
f[i][j] =\sum\limits_{(sum_i-sum_k)\%j==0}f[k][j-1]
f[i][j]=(sumi−sumk)%j==0∑f[k][j−1]
但是这种方法需要枚举 i , j , k i,j,k i,j,k时间复杂度 O n 3 O\ n^3 O n3,显然是不行的
因此我们考虑优化
(
s
u
m
i
−
s
u
m
k
)
%
j
=
=
0
s
u
m
i
%
j
=
=
s
u
m
k
%
j
\begin{aligned} &(sum_i-sum_k)\%j==0\\ &sum_i\%j==sum_k\%j\\ \end{aligned}
(sumi−sumk)%j==0sumi%j==sumk%j
由上面的式子我们可以知道
现在我们只需要处理出满足
s
u
m
[
i
]
%
j
=
=
s
u
m
[
k
]
%
j
sum[i]\%j==sum[k]\%j
sum[i]%j==sum[k]%j所有位置的
d
p
[
k
]
[
j
−
1
]
dp[k][j-1]
dp[k][j−1]的和即可
因此我们再来一个 p r e [ j ] [ t ] pre[j][t] pre[j][t]表示 s u m [ i ] % j = = t sum[i]\%j==t sum[i]%j==t的 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j−1]的和
因为我们是先处理出来 p r e pre pre然后再来更新 k k k的因此,我们需要转换一下 i , j i,j i,j的顺序
code :
const int N = 3e3+10,mod = 1e9+7;
int n;
ll a[N],pre[N][N],dp[N][N],sum[N];
void solve(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i] = sum[i-1] + a[i];
}
pre[0][0] = 1;
for(int j=1;j<=n;j++){
for(int i=1;i<=n;i++){
dp[i][j] = pre[sum[i]%j][j-1];
pre[sum[i]%j][j-1] = (pre[sum[i]%j][j-1]+dp[i][j-1])%mod;
}
}
ll ans = 0;
for(int i=1;i<=n;i++) ans = (ans%mod + dp[n][i]%mod)%mod;
cout<<ans<<endl;
}