==题解==:
此题应该是一个组合问题
设这个数列第一项为x,设 d ∈{ +a, -b } ,则长度为n的序列所有的项为
x,x+d1,x+d2,x+d3,…,x+dn−1
他的和为 nx+(n−1)d1+(n−2)d2+(n−3)d3+…+dn−1=s
因为x的大小我们无法确定,所以我们把公式变形如下
x=(s−((n−1)d1+(n−2)d2+(n−3)d3+…+dn−1))n
求满足这个式子 的方案数有多少,因为x一定是整数,所以 (s−((n−1)d1+(n−2)d2+(n−3)d3+…+dn−1)) % n一定为 0 ,因此推出s % n == (n−1)d1+(n−2)d2+(n−3)d3+…+dn−1的和 % n,也就是两者的模n的余数必须相同
s是确定的,
所以我们最后就是要求(n−1)d1+(n−2)d2+(n−3)d3+…+dn−1这个式子的所有可能的和模n的余数是s%n的结果数
——————————————————分割线——————————————————————
是一个dp组合问题:
状态表示:设f[i][j]为选了i个数,前 i 个 d 的和模 n 的余数为 j 的集合的数量
明确目标:最后求得是f[n−1][s%n]的值
递推关系:
第 i 次选择时可以选 +a 也可以选 -b
如果选 + a,前i个数的和为
[(n−1)d1 +(n−2)d2 +…+(n−(i−1))di−1+(n−i)a]%n≡j%n
(n−1)d1 +(n−2)d2 +…+(n−(i−1))di−1≡j−(n−i)a
因为f[i][j]代表的是组合数量,因为j-(n-i)a是已经确定的数值,所以变化的数量在前面的和里面,可以推出
所以f[i][j]=f[i−1][j−a(n−i)]
同理,如果选-b, f[i][j]=f[i−1][j+b(n−i)]
f[i][j]=f[i−1][j+b(n−i)]+f[i−1][j−a(n−i)]
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010,MOD = 100000007;
int f[N][N]; //设f[i][j]为前i个数的总和模n余数为j的集合数
int get_mod(int a,int b) //求a除以b的正余数
{
return (a%b+b) % b;
}
int main()
{
int n,s,a,b;
cin >> n >> s >> a >> b;
f[0][0] = 1;
for(int i=1; i<n; ++i){
for(int j=0; j<n; ++j){
f[i][j] = ( f[i-1][get_mod(j-a*(n-i),n)] + f[i-1][get_mod(j+b*(n-i),n)] ) % MOD;
}
}
cout<<f[n-1][get_mod(s,n)]<<endl;
return 0;
}
第四行表示写错了吧,应该是x,x+d1,x+d1+d2,…,x+d1+d2…+dn−1di={a,−b}
我想的是假设前i-1项总和为s那么我们需要得到前i-1项的余数,所以先得到前i-1项的和S给S取余后得到S的余数。
就是[S+(n-i)a]%n=j; 你的题解里(大概)是[S+(n-i)a]%n=j%n;这里不太懂了 = =
你说的也是对的,只要能推出那个递推式就行
不影响因为 j 本身也是模 n 得到的小于 n 的数
对对对!啊!对对对!确实!
我懂了~
感谢!
大佬们还是不太懂波动数列这个j-a*(n-i)的含义
可以把它当做常量理解
好的 谢谢大佬
我想知道他是怎么推出来j+(n-1)*b与前i-1项的和模n余数相同的
我也想知道
记前i项的和为x,则有 x%n=j ,第i项为 -(n-1)b , 前i项的和为 x-(-(n-1)b)即 x+(n-1)b , 而 x+(n-1)b % n = j + (n-1)b % n
就是 x+(n-1)b 在模n的情况下同余于 x%n + (n-1)*b,数论里的一些性质,对于加法,减法,乘法 什么时候取模不影响最终取模的结果
举个例子的话, 8%5=3. (8+6)%5=4 == (8%5+6)%5 == (8%5+6%5)% 5
还有(a + b%n) % n == (a + b) % n这种吗,没证出来,请问有链接吗
我看到的只有(a %n + b % n) % n == (a + b) % n
f[i][j] = (f[i - 1][get_mod(j - a(n - i) % n, n)] + f[i - 1][get_mod(j + b(n - i) % n, n)]) % mod这种更严谨一些吧
这个我是记得之前看评论区有人提到过什么时候取模都不会影响取模的结果。证明的话,我自己证了一下.
(a+b%p)%p=(a%p+b%p%p)%p=(a%p+b%p)%p,不知道严不严谨
这些其实都是数论很基础的知识…在哪看我也不知道怎么说,可以搜索模和同余相关的知识,或者你可以直接找一本信安数学基础,里面会有
f[i][j]应该是表示选了前i-1个数,i个d吧
兄弟你好:我看了前面几篇题解,他们没讲清楚f[i][j]=f[i−1][j−a(n−i)]f[i][j]=f[i−1][j−a(n−i)] 这个状态转移方程。[(n−1)d1 +(n−2)d2 +…+(n−i−1)di+1+(n−i)a]%n≡j%n 你这里左边的j不是应该表示余数嘛,为什么又给j%n。。这里看不太懂了!
中间不是等号,是同余符号,y总视频讲的也是同余,所以都 % n
//因为(c+ia)%n=j=j%n 所以两者同余 c+ia≡j(mod n) c同余(j-i*a)mod n
f(i)为什么要等于f(i-1)
状态的转移
多谢
这篇题解有一个地方错了
因为x是整数,所以s与d[1]*(n-1)+…+d[n-1]同余,而不是余数相等
同余代表着整除,不一定余数相等
例如(-3-1)可以被4整除,但-3%4=-3,1%4=1,二者不相等
是我错了,在数论里余数不能为负,C++里取余结果才可以为负
还有就是为什么前面的方案分别取余AC不了
这里的前 n 项 对应的最大的d 是dn−1
长度为n为什么要输出
f[n-1][s mod n]
?这个我懂了
为什么呢兄弟,讲一讲输出n-1而不是n行吗,感谢
题目是要求长度为 n 的数列,但是我们这里的
f[i][j]
指的是只考虑前 i 项 d 的选择, 数列的第一项首项是 x,这个x是不用选的, 后面才开始选 d , x,x+d1, x+d1+d2....., 一共有 n 个数,但是只需要选 n-1 项焕然大悟,醍醐灌顶了兄弟。我理解为可供咱班制行+a,-b操作的只有
n-1个数字,感谢兄弟
原来如此
[(n−1)d1+(n−2)d2+…+(n−i−1)di+1+(n−i)a] 是有问题吧?
[(n−1)d1+(n−2)d2+…+(n−(i−1))di−1+(n−i)a]n 才是对吧?
(n−x)dn−x, x不是递增的吗?
已经更正,感谢
(n−(i−1))di+1的di+1还要更正一下,是di−1
哈哈,行,谢谢
大佬们还是不太懂这个j-a*(n-i)的含义
可以把它当做常量理解
你好我想问一下, f[i][j] = ( f[i-1][get_mod(j-a(n-i),n)] + f[i-1][get_mod(j+b(n-i),n)] ) % MOD; 为啥写成分别%mod就错了