题目说了
个数模
两两互不相同,有点类似0/1背包,每个剩余类只能选一个,可以用
表示
个在
的数和为
的方案数,DP一波。
对于
,若
且
,则对答案的贡献为
为什么呢?考虑给每个数加上 的整数倍之后模 不会发生变化,所以肯定还是符合模 两两互不相同,然而和却神不知鬼不觉地增大了。所以要使总和为 就相当于把 个 分给 个数,套用经典的小球模型就是把 个小球分给 个盒子,可以为空,这个方案数是 ,题目里是有序的,所以还要乘个阶乘。
为方便计算,上式进一步简化就是
代码中的变量名和题目不太一样
//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;
}