简要题意:
每次可以将 (a_i) 减 (1) 或不变。求让 (a_i) 的前缀和 (\% h) 的值在 ([l,r]) 区间中的最多的个数。
E题是个水dp,也不怎样
用 (f_{i,j}) 表示前 (i) 个数中,(igg ( sum_{k=1}^{i} a_k igg ) \% h = j) 的最大答案。
显然,我们从第 (i) 个数入手。(下标出现负数的,在代码中均处理;转移方程中保留)
如果不选,那么 (f_{i,j} = f_{i-1,j-a_i}).
如果选,那么 (f_{i,j} = f_{i-1,j-a_i+1}).
最后,(f_{i,j} gets f_{i,j} + (l leq j space exttt{and} space j<=r))
这是因为,如果当前的这个前缀和在该范围,也算一个答案。
所以:
[ f_{i,j} =
egin{cases}
0 , i=0 space exttt{and} space j=0 \
max{f_{i-1 , j-a_i} , f_{i-1,j-a_i+1}} \
end{cases}
]
防止出现下标负数 (x) ,这样处理:
[x gets (x+h) \%h
]
如果 (x) 是正数,那 (+h) 不影响答案;如果 (x) 是负数,那 (+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
",ans);
return 0;
}