hdu 5230

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值