[abc]AtCoder Beginner Contest 207 E 动态规划前缀和优化

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]=(sumisumk)%j==0f[k][j1]

但是这种方法需要枚举 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} (sumisumk)%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][j1]的和即可

因此我们再来一个 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][j1]的和

因为我们是先处理出来 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;
	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值