ZCC loves hacking
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/131072 K (Java/Others)
Total Submission(s): 126 Accepted Submission(s): 49
Problem Description
Now, a Codefires round is coming to end. ZCC has got C(0≤C≤106) points
by solving all problems. He is amazed that all other contestants in his room have also solved the hardest problem. So he concludes that most contestants are gonna get FST after the contest except the top rated coder Memset137. Before that, ZCC will hack them
to make his score higher. There are N(1≤N≤100000) other
contestants in ZCC‘s room. They are sorted by their ratings(So Memset137 is the N-th contestant). When ZCC successfully hacks the i-th contestant, he will get i points. You may assume ZCC can hack every wrong solution in very little time, and Memset137 is
the contestant with number N. During that time, other contestants won‘t bother him.
Since ZCC is a modest winner, he doesn‘t want his score to be too high. ZCC wonders how many ways are there that he can choose some contestants‘ solutions except the one submitted by Memset137 and hack them so that his final score is between L and R(C≤L≤R<C+N).
Obviously the result can be very large, so please output it modulo 998244353.
Input
The first line contains an integer T(T=100) which
denotes the number of test cases.
For each test case, there will be four integers N(N≤105), C, L, R in
a single line.
For 95% of the test cases, N≤2000.
For 97% of the test cases, N≤50000.
Output
For each test case, print a single integer which is the answer.
Sample Input
3 3 0 1 2 5 13 14 17 100 0 23 59
Sample Output
2 6 90567
官方题解
因为第N个人的作用仅仅是丰富题面,可以直接把N减1。C的作用仅仅是为了使情景更逼真,可以直接把L和R减去C。 显然选的人的个数最多是O(n??√)的。 题目中有个条件,保证了想要选的数的总和不会超过N。考虑类似于背包的dp。令dp[i][j]表示现在已经选了最大的i个想选的数,和为j时的方案数。转移的时候,要么把之前选择的每一个数增加一,要么在把之前选择的每一个数都增加一个基础上,再新加入一个当前大小为1的数。 若最后选的个数为i,那么就对答案产生∑Rj=Ldp[i][j]的贡献。累加即可。注意一个都不选也是合法的。 复杂度O(nn??√).
PS:解释下转移方程,由于选的值是不能重复,直接用背包肯定超时,这里就用了一个巧妙的方法,移位,将i
个数右移1位,即每个数都增加1,即是dp[i][j+i],右移一位后最前面会空出1位,这时在最前面增加一个数
1,因为经过移位,所有的数都比1大,所以不会出现重复,即dp[i+1][j+i+1].
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int maxn=100000+100; const int mod= 998244353; const int maxm=450; int dp[2][maxn+5]; int s[maxn+5]; int main() { int t,n; dp[1][1]=1; int it=1;//滚动数组标记 for(int i=1; i<=maxm; i++,it=!it) { for(int j=1; j<=maxn; j++) { if(j+i<=maxn) dp[it][j+i]=(dp[it][j+i]+dp[it][j])%mod; if(j+i+1<=maxn) dp[!it][j+i+1]=(dp[it][j]+dp[!it][j+i+1])%mod; s[j]=(s[j]+dp[it][j])%mod;//取的值为j的和 dp[it][j]=0;//为下一行初始化 } } for (int j = 1; j < maxn; j++)//前缀和 s[j] = (s[j] + s[j - 1]) % mod; scanf("%d",&t); while(t--) { scanf("%d",&n); int c,l,r; scanf("%d%d%d",&c,&l,&r); r=r-c; l=l-c; int ans=s[r]; if(l>0) ans=(ans-s[l-1]+mod)%mod; if(l==0)//全部不取时 ans++; printf("%d\n",ans); } return 0; }