TopCoder SRM 572 Div2 1000 DistinctRemainders

题目说了 K K 个数模m两两互不相同,有点类似0/1背包,每个剩余类只能选一个,可以用 f[i][j] f [ i ] [ j ] 表示 i i 个在[0,M1]的数和为 j j 的方案数,DP一波。
对于f[i][j],若 jN(mod M) j ≡ N ( m o d   M ) j<=N j <= N ,则对答案的贡献为

f[i][j]C((Nj)/M+i1,i1)i! f [ i ] [ j ] ∗ C ( ( N − j ) / M + i − 1 , i − 1 ) ∗ i !

为什么呢?考虑给每个数加上 m m 的整数倍之后模m不会发生变化,所以肯定还是符合模 m m 两两互不相同,然而和却神不知鬼不觉地增大了。所以要使总和为N就相当于把 (Nj)/M ( N − j ) / M M M 分给i个数,套用经典的小球模型就是把 (Nj)/M ( N − j ) / M 个小球分给 i i 个盒子,可以为空,这个方案数是C((Nj)/M+i1,i1),题目里是有序的,所以还要乘个阶乘。

为方便计算,上式进一步简化就是 f[i][j]A((Nj)/M+i1,i1)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值