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 ; }
以上.
相关文章推荐
- adb不显示连接的设备
- NoSQL笔记
- 给小伙伴发福利啦!
- WIN7 32位 mysql-5.7.11压缩版安装
- Handler传递数据更新进度条
- C++函数后面加const关键字
- 【通信15】JAVA OOP授课计划
- qt宽字符串中文乱码(codec->toUnicode值得学习)
- Android_问卷调查
- Android ListView StickyListHeaders使用Demo
- Web开发者推荐的最佳HTML5/CSS3代码生成器
- js中二维数组的转置
- Ansible自动化运维(一)
- Android颜色对照表
- DataX 介绍
- wifidog 认证
- (Caffe,LeNet)初始化训练网络(三)
- 数组G结构体
- "javax.servlet.http.HttpServlet" was not found on the Java Build Path
- RxAndroid使用入门记录