Hack比赛(2020.02.05【NOIP普及组】模拟赛C组)

题目描述

Codefires round马上就要结束了!ZCC已经通过做题得到了C分。但他惊讶地发现,房间里的其他选手也都解决了最难的题。ZCC根据经验断定大多数的选手都会FST的!当然,除去rating最高的选手Memset137。在此之前,ZCC想要hack他们来使他的得分更高。除了ZCC,房间里有N个选手,他们已经被按照rating从小到大排序了(所以Memset137是排在第N个的选手)。当ZCC成功hack了第i个选手时,他会获得i分的收入。你可以假设ZCC hack技术高超,百发百中,可以hack除了Memset137和自己以外的所有选手,而且在此期间没有其他选手干扰。
由于ZCC有着谦虚的美德,他不想让自己的得分太高。ZCC想要知道,存在着多少种不同的选择一些人hack的方案,使得他的得分在L和R(C≤L≤R<C+N)之间。
显然答案会很大,请输出答案对998244353取模后的结果。

输入

一行,四个整数n,c,l,r分别表示n个选手,当前分数为c,希望得分在l到r之间

输出

输出一行,方案数

样例输入

Sample Input1:
3 0 1 2

Sample Input2:
5 13 14 17

Sample Input3:
100 0 23 59

样例输出

Sample Output1:
2

Sample Output2:
6

Sample Output3:
90567

数据范围限制

对于20%的数据: 1≤N≤50;
对于50%的数据: 1≤N≤2000;
对于100%的数据: 1≤N≤100000, 1≤C≤1000000,C≤L≤R<C+N;

*分析:以前的整数划分,一般就是dp[i][j]表示i这个数字,最小的拆分数是j的时候,拥有的方案数,可以控制其没有重复数字,但是空间复杂度太大。
用一种新的方法
dp[i][j]表示j这个数字,当前的拆分拥有i个拆分数时的方案数。至于为什么要在第二维中放j这个数字,而不是像上面那个用第一维放数字,那是因为它需要先解出dp[1][1—n]然后再解出dp[2][1—n],转换维度比较方便。
先考虑允许重复数字 : dp[i][j] = dp[i][j - i] + dp[i - 1][j - 1];
考虑分成两类
1、dp[i][j - i]:这种拆分方案(拥有i个数字的拆分方案),如果没有1,就比如7 = 3 + 4这样,然后每个数字都加上一,
就变成了9 = 5 + 4。所以dp[2][9]可以由dp[2][7]转化过来。当然7 = 1 + 6也是合法解。
2、dp[i - 1][j - 1]:这种拆分方案有1,比如4 = 3 + 1,那么我可以截去那个1,变成3 = 3,然后加上最后那个1,就变成了
4 = 3 + 1,所以dp[2][4]可以由dp[1][3]转化过来。
但是题目需要不重复,(这也使得题目不会超时)
第一类,如果dp[2][7]本来就是不重复的,就是dp[2][6]即是6 = 3 + 3不能发生,那么我同时全部加上一个数,肯定不会产生重复的。
第二类,如果本来也是不重复的,但是生成的可能会重复,比如5 = 4 + 1和5 = 3 + 2是dp[2][5]的解(本来没有重复),然后在后面加上一个1,是dp[3][6]的解,但是6 = 4 + 1 + 1是非法的。我们也不可能检查其拆分方案有没1,因为我们只会统计数量。

改进
dp[i][j] = dp[i][j - i] + dp[i - 1][j - i];
dp[i - 1][j - i]:意思就是每个元素减去1后,分成i - 1组,为什么不是分成i组呢。?因为其存在1,然后每个数字减去1,那么这个1就是变成0了,所以只能分成i - 1组。
例如:6 = 3 + 2 + 1是由3 = 2 + 1弄过来的,dp[3][6] = dp[2][3]
所以就能解决这题。
1、i > j,不用管dp[i][j] = 0
2、i == j,只能够是i个1,然而不符合题目,dp[i][j] = 0;
3、i < j,dp[i][j] = dp[i][j - i] + dp[i - 1][j - i];
由于之和上一维有关,可以滚动数组,就能解决空间问题。
然后因为要产生k个不同的数字,最小值是1 + 2 + 3 + … + k,就是(1 + k) * k / 2开始。
就是dp[2][2]这些不用枚举了,k = 2的话,最小值是3
初始条件 dp[0][0] = 1;*

code:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int M=998244353;
int f[2][100005],n,c,l,r,s;
int main()
{
	freopen("hack.in","r",stdin);
	freopen("hack.out","w",stdout);
	cin>>n>>c>>l>>r;
	l-=c;r-=c;
	f[0][0]=1;
	for(int i=1;(1+i)*i/2<=r;i++)
    {
        for(int j=(1+i)*i/2;j<=r;j++)
        {
            f[i&1][j]=(f[i&1][j-i]+f[(i-1)&1][j-i])%M;
            if(l<=j&&j<=r) s=(s+f[i&1][j])%M;
        }
        memset(f[(i-1)&1],0,sizeof(f[(i-1)%1]));
    }
	cout<<s%M<<endl;  
	return 0;
}

谢谢

发布了80 篇原创文章 · 获赞 58 · 访问量 2546

猜你喜欢

转载自blog.csdn.net/bigwinner888/article/details/104201358