[ABC419E] E - Subarray Sum Divisibility
题目大意
给定一个长度为 \(N\) 的整数序列 \(A = (A_1, A_2, \ldots, A_N)\) 。
可以多次选择一个整数 \(i\) 使 \(1 \leq i \leq N\) ,并将 \(A_i\) 的值增加 \(1\) 。
以便对于每个长度为 \(L\) 的连续子数组 \(A\) ,求和是 \(M\) 的倍数。
在达到目标之前找到最小可能的操作次数。
分析
看到数据范围 \(1 \leq N, M \leq 500\),很明显,应该是 \(O(N^3)\) 级别的做法,容易想到使用动态规划解决这个问题。
观察一下满足条件的数列的性质,发现当 \(i>L\) 是,有 \(A_i \equiv A_{i-L} \pmod{M}\),所以确定了前 \(L\) 个数确定后即可确定后面的数。
考虑计算前 \(L\) 个数中的数改变后,对后面的数产生的影响而所产生的代价(包括自己)。
记 \(c_{i,j}\) 表示,下标模 \(L\) 为 \(i\),数字(即所有对应的 \(A_i\))模 \(M\) 为 \(j\),所需要的代价。
这是容易计算的(实在不会见代码)。
接下来计算答案(只处理前 \(L\) 个),我们设 \(f_{i,j}\) 表示处理到第 \(i\) 个数,前 \(i\) 个数模 \(M\) 为 \(j\) 的最小代价。
则有转移:
\[f_{i,(j+k) \% M} = \min(f_{i-1,(j+k) \% M},f_{i-1,j}+c_{i,k})
\]
其实本质上是一个背包问题。
最终答案为 \(f_{L,0}\)。
下标建议从 \(0\) 开始,方便实现。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e2+7;
const int inf=1e9+7;
int a[MAXN],n,l,m,f[MAXN][MAXN],c[MAXN][MAXN],g[MAXN];
signed main()
{
scanf("%d %d %d",&n,&m,&l);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<=500;i++)
for(int j=0;j<=500;j++)
f[i][j]=inf;
for(int i=0;i<l;i++)
for(int j=i;j<n;j+=l)
for(int k=0;k<m;k++)
c[i][(a[j]+k)%m]+=k;
f[0][0]=0;
for(int i=1;i<=l;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
f[i][(j+k)%m]=min(f[i][(j+k)%m],f[i-1][j]+c[i-1][k]);
printf("%d",f[l][0]);
return 0;
}
谢谢观看!

浙公网安备 33010602011771号