我们约定:
- \(f_{l,r}\) 表示 \([l,r]\) 最多可以进行的操作次数(不一定要全部消掉)。
- \(s_{l,r}\) 表示 \([l,r]\) 的 \(a\) 的和。
考虑 \(f\) 应该怎么求解,根据区间 DP 的套路我们枚举中间点:
\[f_{i,j}=\max\limits_{k=i}^{j-1} f_{i,k}+f_{k+1,j}
\]
显然,我们还需要处理这一次合并之后新增的操作次数。
如果 \(s_{i,j}\equiv r\times (f_{i,j}+1) \pmod d\),那么我们就可以多操作一次,即:
\[f_{i,j}\gets f_{i,j}+[s_{i,j}\equiv r\times (f_{i,j}+1) \pmod d]
\]
引理:
一个区间可以被操作 \(c_0\) 次操作完,当且仅当 \(c_0\) 满足:
\[c_0\times r\equiv s_{i,j} \pmod{d}\wedge c_0\le f_{i,j} \]因为 \(c_0\le f_{i,j}\),\(c_0\) 次操作肯定是可以进行的,我们只需要证明进行 \(c_0\) 次操作可以把 \([i,j]\) 全部消掉。
那么我们不妨先随便进行 \(c_0-1\) 次操作,那么消除的和一定是 \(r\times (c_0-1)+kd\),其中 \(k\) 是一个非负整数。所以我们剩下的显然会是 \(r+k'd\)(因为第一个条件),所以就可以一次全部消掉。
假设最终的序列进行了 \(k\) 次操作,那么最后我们获得的收益就是:
\[\frac{s_{1,n}-kr}{d}
\]
显而易见,我们应该让 \(k\) 尽可能小,于是就有了一个天才般的想法。
我们给每一个区间求出满足条件的最小的 \(c_0\)(没有满足条件的取 \(+\infty\)),然后再进行一次区间 DP 即可。
对于 \(c_0\) 具体的求解,因为 \(c_0\) 一定是 \([1,n]\) 中的整数,我们直接枚举所有的可能然后预处理出来就行了。
时间复杂度为 \(O(n^2)\),模拟赛的时候饭堂了,没有想到。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
int n,d,r,a[N],f[N][N],g[N][N];
unordered_map<int,int> c0;
int s(int l,int r){return a[r]-a[l-1];}
signed main(){ios::sync_with_stdio(false);cin.tie(nullptr);cin>>n>>d>>r;for(int i=1,s=0;i<=n;i++){cin>>a[i];if(a[i]%d==r) f[i][i]=1,g[i][i]=a[i]/d;s=(s+r)%d,a[i]+=a[i-1];if(!c0[s]) c0[s]=i;}for(int len=2;len<=n;len++){for(int i=1;i+len-1<=n;i++){int j=i+len-1,c0=::c0[s(i,j)%d];for(int k=i;k<j;k++){g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]);f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);}f[i][j]+=!((s(i,j)-(f[i][j]+1)*r)%d);if(c0&&c0<=f[i][j]){g[i][j]=max(g[i][j],(s(i,j)-c0*r)/d);}}}cout<<g[1][n]<<'\n';return 0;
}