题目说了
K
K
个数模两两互不相同,有点类似0/1背包,每个剩余类只能选一个,可以用
f[i][j]
f
[
i
]
[
j
]
表示
i
i
个在的数和为
j
j
的方案数,DP一波。
对于,若
j≡N(mod M)
j
≡
N
(
m
o
d
M
)
且
j<=N
j
<=
N
,则对答案的贡献为
为什么呢?考虑给每个数加上 m m 的整数倍之后模不会发生变化,所以肯定还是符合模 m m 两两互不相同,然而和却神不知鬼不觉地增大了。所以要使总和为就相当于把 (N−j)/M ( N − j ) / M 个 M M 分给个数,套用经典的小球模型就是把 (N−j)/M ( N − j ) / M 个小球分给 i i 个盒子,可以为空,这个方案数是,题目里是有序的,所以还要乘个阶乘。
为方便计算,上式进一步简化就是 f[i][j]∗A((N−j)/M+i−1,i−1)∗i f [ i ] [ j ] ∗ A ( ( N − j ) / M + i − 1 , i − 1 ) ∗ i
代码中的变量名和题目不太一样
//tc is healthy, just do it
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2501;
const int M=51;
const int p=1e9+7;
ll f[M][N];
class DistinctRemainders {
public:
int howMany( long long N, int M );
};
void Add(ll &x,ll y){
x+=y;
while(x>=p) x-=p;
}
ll A(ll n,int m){
if (n<m) return 0;
ll ans=1;
for(int i=1;i<=m;i++)
ans=ans*((n-i+1)%p)%p;
return ans;
}
int DistinctRemainders::howMany(long long s, int m) {
f[0][0]=1;
int sum=0;
for(int i=0;i<m;i++){
sum+=i;
for(int j=i+1;j;j--) //这里要倒着for,原理类似于0/1背包的倒着for
for(int k=i;k<=sum;k++)
Add(f[j][k],f[j-1][k-i]);
}
ll y=s%m;
ll ans=0;
for(int i=1;i<=m;i++)
for(ll j=y;j<=s&&j<=sum;j+=m)
Add(ans,f[i][j]*A((s-j)/m+i-1,i-1)%p*i%p);
return (int)ans;
}