ZCC loves hacking
题目描述:
其实就是给了n~100000,c,l,r,其中C≤L≤R
题解:
所有情况数,刚开始一定会想dp【i】【j】用到数i达到和j的背包的算法,但是发现太大了。而且没有很好的利用1到n连续的性质。怎么用呢?不能用组合数推,那么还是想办法用dp,只是要改变一下定义状态,这个思想很重要。dp【i】【j】,用i个数达到和j的总个数。只用一个和不能够描述,加上用了几个数,因为用的个数大约为sqrt(n),本题大约是500,然后试着用这个dp推一下,如果dp【i】【j】中没有1,那么直接dp【i】【j-i】,如果有1,那么必须加上dp【i-1】【j-i】,这点一定要搞清楚。之后求个和就行了。
重点:
不能够枚举用到几来dp,或许会想到枚举用了几个数,发现最多用sqrt(n)个数,然后试着写用n个数搞出和为x,仍然要想到用dp,减少规模,发现集体减一能够保持不同性,再考虑上1,就搞定了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const ll maxn = 1e5 +10;
const ll M = 998244353;
const ll key = 500;
ll dp[2][maxn];
ll now, last;
ll n, l, r, c;
ll ans;
void getDp()//用的是滚动数组,初始化00为1.
{
CLR(dp);
now = 0;
last = 1;
CLR(dp[last]);
dp[last][0] = 1;
ans = 0;
if(l==0)
{
ans = 1;
}
REP_D(i, 1, key)
{
REP_D(j, 0, n)
{
if(j-i<0)
{
dp[now][j]=0;
}
else
{
dp[now][j]=(dp[last][j-i]+dp[now][j-i])%M;
}
}
REP_D(j, l, r)//一定要边dp边加。
{
ans = (ans + dp[now][j])%M;
}
swap(now, last);
}
}
void solve()
{
l -= c;
r -= c;
getDp();
printf("%I64d\n", ans);
}
int main()
{
freopen("9Iin.txt", "r", stdin);
//freopen("9Iout.txt", "w", stdout);
ll ncase;
scanf("%I64d", &ncase);
while(ncase--)
{
scanf("%I64d%I64d%I64d%I64d", &n, &c, &l, &r);
solve();
}
return 0;
}