题意分析
对于本题我们可以列出下列等式:
x
+
(
x
+
d
1
)
+
(
x
+
d
1
+
d
2
)
+
.
.
.
+
(
x
+
d
1
+
d
2
+
.
.
.
+
d
n
−
1
)
=
s
x+(x+d_1)+(x+d_1+d_2)+...+(x+d_1+d_2+...+d_{n-1})=s
x+(x+d1)+(x+d1+d2)+...+(x+d1+d2+...+dn−1)=s
其中,
d
=
{
+
a
,
−
b
}
d=\{ +a,-b\}
d={+a,−b}
变形得:
n
x
+
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
d
n
−
1
=
s
nx+(n-1)d_1+(n-2)d_2+...+d_{n-1}=s
nx+(n−1)d1+(n−2)d2+...+dn−1=s
对于一个式子中有两个变量
x
,
d
i
x,d_i
x,di,利用式子可以转换:
x
=
s
−
(
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
d
n
−
1
)
n
x=\frac{s-((n-1)d_1+(n-2)d_2+...+d_{n-1})}{n}
x=ns−((n−1)d1+(n−2)d2+...+dn−1)
根据上式,我们可以得出信息:对于一定的n和s,一组
d
i
d_i
di对应着确定
x
x
x,有多少组符合条件的
d
i
d_i
di就是题目的总方案数,所以该问题可以转换成组合问题,即
d
1
.
.
.
d
i
d_1...d_i
d1...di的组合情况,每个
d
d
d有两种情况,取+a或者-b
注意,题目有对
x
x
x的约束,即
x
x
x必须为整数,所以要求
s
−
(
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
d
n
−
1
)
s-((n-1)d_1+(n-2)d_2+...+d_{n-1})
s−((n−1)d1+(n−2)d2+...+dn−1)必须是
n
n
n的倍数,即
(
n
−
1
)
d
1
+
(
n
−
2
)
d
2
+
.
.
.
+
d
n
−
1
(n-1)d_1+(n-2)d_2+...+d_{n-1}
(n−1)d1+(n−2)d2+...+dn−1和
s
s
s对
n
n
n取模的值要相等。
所以,根据闫氏DP分析法,我们进行下述假设:
- 状态表示(确定集合): f ( i , j ) f(i,j) f(i,j)表示在前 i i i项,余数为 j j j的组合数,最终的结果就是 f ( n − 1 , s % n ) f(n-1,s\%n) f(n−1,s%n)
- 状态计算(子集划分):
f
(
i
,
j
)
f(i,j)
f(i,j)的所有情况可以划分为第
i
i
i项的
d
d
d是
+
a
+a
+a,和第
i
i
i项的
d
d
d是
−
b
-b
−b,两种情况,而这两种情况又分别对应
f
(
i
−
1
,
(
j
−
(
n
−
i
)
∗
a
)
%
n
)
f(i-1,(j-(n-i)*a)\%n)
f(i−1,(j−(n−i)∗a)%n)和
f
(
i
−
1
,
(
j
+
(
n
−
i
)
∗
b
)
%
n
)
f(i-1,(j+(n-i)*b)\%n)
f(i−1,(j+(n−i)∗b)%n)
即 f ( i , j ) = f ( i − 1 , ( j − i ∗ a ) % n ) + f ( i − 1 , ( j + i ∗ b ) % n ) f(i,j)=f(i-1,(j-i*a)\%n)+f(i-1,(j+i*b)\%n) f(i,j)=f(i−1,(j−i∗a)%n)+f(i−1,(j+i∗b)%n)
Q:为什么前 i − 1 i-1 i−1项的和的余数是 ( j − ( n − i ) ∗ a ) % n (j-(n-i)*a)\%n (j−(n−i)∗a)%n呢?
A:假设前 i − 1 i-1 i−1项的和为 C C C,那么就有 ( C + ( n − i ) ∗ a ) % n = j (C+(n-i)*a)\%n=j (C+(n−i)∗a)%n=j,即 C % n = ( j − ( n − i ) ∗ a ) % n C\%n=(j-(n-i)*a)\%n C%n=(j−(n−i)∗a)%n
至此,所有分析就已经结束,可以直接写代码了,其他细节在代码中完善。
AC CODE
#include <iostream>
using namespace std;
const int N=1005;
#define p 100000007
int f[N][N];
//获取a除以b的正的正余数
int get_mod(int a,int b)
{
return (a%b+b)%b;
}
int main()
{
int n,s,a,b;
cin>>n>>s>>a>>b;
//初始化,长度为0,余数也为0的情况方案为1
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-(n-i)*a,n)] + f[i-1][get_mod(j+(n-i)*b,n)])%p;
cout<<f[n-1][get_mod(s,n)];
return 0;
}