您的位置:首页 > 其它

JZOJ3851. 发奖金(reward)

2016-04-07 22:51 190 查看

题目大意

有m元与n个员工,将这m元钱当做奖金发给每一个员工,钱可不全部发完,而且可能有员工没有奖金,求可能的方案数。最后答案%p

以m=1, n=2为例:

方案1:员工1发1元,员工2发0元

方案2:员工1发0元,员工2发1元

方案3:员工1发0元,员工2发0元

Data Constraint

对于p:设p=pc11∗pc22∗pc33∗…∗pctt , pi为质数。

20%的数据:1≤n,m≤15;

40%的数据:1≤n,m≤1000,p=10007;

60%的数据:保证t=1,ci=1,pcii≤105;

80%的数据:t≤2,ci=1,pi≤105;

100%的数据:1≤n,m≤109,1≤pcii≤105,所有P不超过231−1。

题解

考虑插板法:

可以将奖金视为m个小球,由于老板可以自己留钱,所以可以看为n+1个人分m元。由挡板原理易得最终方案数为Cnm+n。

但是这题的p不一定是质数,所以不能直接求逆元或者Lucas定理。

这里介绍一种组合数取模的方法。

设p=pc11∗pc22∗pc33∗…∗pctt

对于每一个pcii分开考虑,最后用中国剩余定理合并答案即可。

设f[n]表示不包含pi因数的n!

∴Cnm+n=(m+n)!m!n!=pkif[m+n]f[m]f[n]=pki∗f[m+n]∗(f[m]f[n])−1

其中k表示分子提取出的因数个数与分母之差,即上下约分后剩余的pi。

然后f与k都可以递归求出。

补充几点:

f可以预处理,尤其对于多组数据,这样做能大幅度提高效率。

求逆元时用欧拉定理比较简单。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;

typedef long long ll ;

ll Pri[20][3] , A[20] ;
ll n , m , L , P , MO , ans , tot ;

ll Power( ll x , ll k ) {
ll s = 1 ;
while ( k ) {
if ( k % 2 == 1 ) s = s * x % MO ;
x = x * x % MO ;
k /= 2 ;
}
return s ;
}

ll phi( ll p , ll X ) { return X / p * (p - 1) ; }

ll count( ll n , int p ) { return n == 0 ? 0 : n / p + count( n / p , p ) ; }

ll fac( ll n , int p ) {
if ( !n ) return 1 ;
ll ret = 1 ;
for (int i = 1 ; i <= MO ; i ++ ) {
if ( i % p == 0 ) continue ;
ret = ret * i % MO ;
}
ret = Power( ret , n / MO ) ;
for (int i = (n / MO) * MO + 1 ; i <= n ; i ++ ) {
if ( i % p == 0 ) continue ;
ret = ret * i % MO ;
}
return ret * fac( n / p , p ) % MO ;
}

int main() {
scanf( "%I64d%I64d%I64d" , &n , &m , &P ) ;
int up = min( 1e5 , sqrt(P) ) , now = P ;
for (int i = 2 ; i <= up && now > 1 ; i ++ ) {
if ( now % i ) continue ;
Pri[tot+1][2] = 1 ;
while ( now % i == 0 ) {
Pri[tot+1][0] ++ ;
Pri[tot+1][2] *= i ;
now /= i ;
}
Pri[++tot][1] = i ;
}
if ( now > 1 ) {
Pri[++tot][0] = 1 ;
Pri[tot][1] = now ;
Pri[tot][2] = now ;
}
L = n + m ;
for (int i = 1 ; i <= tot ; i ++ ) {
MO = Pri[i][2] ;
ll k = count( L , Pri[i][1] ) - count( n , Pri[i][1] ) - count( m , Pri[i][1] ) ;
A[i] = Power( Pri[i][1] , k ) * fac( L , Pri[i][1] ) % MO ;
A[i] = A[i] * Power( fac( n , Pri[i][1] ) * fac( m , Pri[i][1] ) % MO , phi( Pri[i][1] , Pri[i][2] ) - 1 ) % MO ;
ans = (ans + A[i] * (P / MO) % P * Power( P / MO , phi( Pri[i][1] , Pri[i][2] ) - 1 ) % P) % P ;
}
printf( "%I64d\n" , ans ) ;
return 0 ;
}


以上.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: