一道非常好的dp题 首先可以看出c是没什么用的 用l和r减去c得到真正的l,r即可
注意到l,r的范围 是小于n,c的 即真正的l,r是小于n的 而所求的问题转换成 1到n的数中 不重复的选取 有多少种选取方案的总和位于l,r之间
从而可以转化为 求有多少种选取方案的总和小于等于x 设为f(x) 答案即为 f(r)-f(l-1)
而我们先可以求出有多少种选取方案的总和等于x 设为 g(x),而g(x)的前缀和即为f(x)
而g(x)要通过dp来求解 dp[i][j]表示选取i个数 总和为j的方案数
因为我们是选取了i个不同的数 总和j的范围是n=100000 所以i的数量应该小于根号(2n)的 (前i项和i(i+1)/2<=n) 即n根号n的复杂度
但我们如果保证dp时不重不漏呢?
有这样一种神奇的dp方式 开始的时候有一个大小为1的数 然后我们每次操作要么把所有的数+1 要么就把所有的数+1在添加一个新的数1
这样就可以不重不漏的枚举到所有的情况 当我们访问dp[i][j]时 把dp[i][j]的值赋给 g(x)即可
最后注意下l为0时的情况 不能访问l-1
还有同样的代码时T时过的 可以再加个输入输出优化
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#define scnaf scanf
#define cahr char
#define bug puts("bugbugbug");
using namespace std;
typedef long long ll;
const int maxn=100005;
ll mod=998244353;
ll dp[2][maxn], ans[maxn];
void init()
{
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
int N=100000;
dp[1][1]=1;
for(int i=1;i<=450;i++)
{
int now=i%2;
int next=!now;
for(int j=0;j<=N;j++) dp[next][j]=0;
for(int j=1;j<=N;j++)
{
ans[j] = (ans[j]+dp[now][j])%mod;
if(j+i<=N)
dp[now][j+i] =(dp[now][j+i]+dp[now][j])%mod;
if(i+j+1<=N)
dp[next][j+i+1] =(dp[next][j+i+1]+dp[now][j])%mod;
}
}
for(int i=1;i<=N;i++)
ans[i]=(ans[i]+ans[i-1])%mod;
}
int main()
{
init();
int T_T;
scanf("%d",&T_T);
while(T_T--)
{
int n,C,l,r;
scanf("%d%d%d%d",&n,&C,&l,&r);
l-=C; r-=C;
if(l==0)
cout<<(ans[r]+1)%mod;
else
cout<<(ans[r]-ans[l-1]+mod)%mod;
puts("");
}
}