简要题意:
每次可以将 a i a_i ai 减 1 1 1 或不变。求让 a i a_i ai 的前缀和 % h \% h %h 的值在 [ l , r ] [l,r] [l,r] 区间中的最多的个数。
E题是个水dp,也不怎样
用 f i , j f_{i,j} fi,j 表示前 i i i 个数中, ( ∑ k = 1 i a k ) % h = j \bigg ( \sum_{k=1}^{i} a_k \bigg ) \% h = j (∑k=1iak)%h=j 的最大答案。
显然,我们从第 i i i 个数入手。(下标出现负数的,在代码中均处理;转移方程中保留)
如果不选,那么 f i , j = f i − 1 , j − a i f_{i,j} = f_{i-1,j-a_i} fi,j=fi−1,j−ai.
如果选,那么 f i , j = f i − 1 , j − a i + 1 f_{i,j} = f_{i-1,j-a_i+1} fi,j=fi−1,j−ai+1.
最后, f i , j ← f i , j + ( l ≤ j and j < = r ) f_{i,j} \gets f_{i,j} + (l \leq j \space \texttt{and} \space j<=r) fi,j←fi,j+(l≤j and j<=r)
这是因为,如果当前的这个前缀和在该范围,也算一个答案。
所以:
f i , j = { 0 , i = 0 and j = 0 max f i − 1 , j − a i , f i − 1 , j − a i + 1 f_{i,j} = \begin{cases} 0 , i=0 \space \texttt{and} \space j=0 \\ \max{f_{i-1 , j-a_i} , f_{i-1,j-a_i+1}} \\ \end{cases} fi,j={0,i=0 and j=0maxfi−1,j−ai,fi−1,j−ai+1
防止出现下标负数 x x x ,这样处理:
x ← ( x + h ) % h x \gets (x+h) \%h x←(x+h)%h
如果 x x x 是正数,那 + h +h +h 不影响答案;如果 x x x 是负数,那 + h +h +h 变为正数,答案也正确。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+1;
inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int n,h,l,r,ans=0;
int a[N],f[N][N];
int main(){
n=read(),h=read(),l=read(),r=read();
for(int i=1;i<=n;i++) a[i]=read();
memset(f,-63,sizeof(f)); //预处理为极小值
f[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<h;j++)
f[i][j]=max(f[i-1][(j-a[i]+h)%h],f[i-1][(j-a[i]+1+h)%h])+(l<=j && j<=r);
for(int i=0;i<h;i++) ans=max(ans,f[n][i]); //将 1~n 的答案取最大值
printf("%d\n",ans);
return 0;
}