组合数取模&&Lucas定理题集
题集链接:
https://cn.vjudge.net/contest/231988
解题之前请先了解组合数取模和Lucas定理
A : FZU-2020
输出组合数C(n, m) mod p
(1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数)
由于p较大,不可以打表,直接Lucas求解
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + ;
ll fac[maxn];//阶乘打表
void init(ll p)//此处的p应该小于1e5,这样Lucas定理才适用
{
fac[] = ;
for(int i = ; i <= p; i++)
fac[i] = fac[i - ] * i % p;
}
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - , p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return ;
ll up = , down = ;//分子分母;
for(int i = n - m + ; i <= n; i++)up = up * i % p;
for(int i = ; i <= m; i++)down = down * i % p;
return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == )return ;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
int main()
{
ll n, m, p;
int T;
cin >> T;
while(T--)
{
cin >> n >> m >> p;
//init(p);
cout<<Lucas(n, m, p)<<endl;
}
return ;
}
B:HDU-3944
求从顶点到达第n行,第m列的元素的最短路径值(最开始是第0行,第0列)
只能向下或者斜向右。
如图,如果在右侧,可以走红色路线,比如第4行第3列,与此对称的是第4行第1列(从第0行,第0列数起)
这样,我们就可以把左侧的转化成右侧来统一计算。中间的也可以这样计算。
左侧转化成右侧:对于n行m列而言,如果m * 2 < n 那么m = n - m
对于右侧的如何计算呢?
用C(x, y)表示x行y列的值,也就是组合数的值
那么ans = C(n , m) + C(n - 1, m) + C(n - 2, m) + ... + C(m + 1, m) + C(m, m) + C(m - 1, m - 1) + C(m - 2, m - 2) + C(0, 0)
红色部分为m
蓝色部分可以通过组合数公式C(n, m) + C(n, m + 1) = C(n +1, m + 1)来计算
把蓝色部分的C(m, m)写成C(m + 1, m + 1)
那么最后两项就可以通过上面那个公式合并成C(m + 2, m + 1)
不断合并,最后得到C(n, m) + C(n, m + 1) = C(n +1, m + 1)
所以答案就是C(n + 1, m + 1) + m % p;
由于计算量大,p的范围是10000内的素数,而需要计算10^6个组合数,所以需要先打表,把10000内的素数筛选出来,打表每个阶乘模上素数的值,还有逆元,这样计算组合数才不会超时,而且数组不能开long long,会超时
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + ;
int fac[maxn][maxn], inv[maxn][maxn];//阶乘打表 逆元打表
int prime[maxn], pth[maxn];
int pow(int a, int b, int m)
{
int ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
bool is_prime[maxn];
void init()
{
int n = , cnt = ;
for(int i = ; i <= n; i++)is_prime[i] = ;
for(int i = ; i <= n; i++)
{
if(is_prime[i])
{
prime[++cnt] = i;
pth[i] = cnt;
for(int j = i * i; j <= n; j += i)is_prime[j] = ;
}
}
//cout<<cnt<<endl;
for(int i = ; i <= cnt; i++)
{
fac[i][] = inv[i][] = ;
for(int j = ; j < prime[i]; j++)//大于i的阶乘模上i均为0
{
fac[i][j] = fac[i][j - ] * j % prime[i];
inv[i][j] = pow(fac[i][j], prime[i] - , prime[i]);
}
}
} int C(ll n, int m, int p)//组合数C(n, m) % p
{
if(m > n)return ;
if(m == n)return ;
int t = pth[p];
return fac[t][n] * (inv[t][m] * inv[t][n - m] % p) % p;
} int Lucas(int n, int m, int p)
{
//cout<<n<<" "<<m<<" "<<p<<endl;
if(m == )return ;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
} int main()
{
init();
int n, m, p;
int cases = ;
while(scanf("%d%d%d", &n, &m, &p) != EOF)
{
if(m <= n / )
{
m = n - m;
}
ll ans = (Lucas(n + , m + , p) + m) % p;
printf("Case #%d: %d\n", ++cases, ans);
}
return ;
}
C:ZOJ-3557
在n个元素的集合中取出lm和不相邻元素的种数
插空法,由于不相邻,那就把这m个先取出来,放在剩下的n-m个数字的两侧的空位上,那么这m个一定不相邻。
等价于n-m+1中放m个元素
C(n - m + 1, m) % p
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
const int mod = 1e9 + ;
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll inv(ll x, ll p)//x关于p的逆元,p为素数
{
return pow(x, p - , p);
}
ll C(ll n, ll m, ll p)//组合数C(n, m) % p
{
if(m > n)return ;
ll up = , down = ;//分子分母;
for(int i = n - m + ; i <= n; i++)up = up * i % p;
for(int i = ; i <= m; i++)down = down * i % p;
return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m, ll p)
{
if(m == )return ;
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
int main()
{
ll n, m, p;
while(cin >> n >> m >> p)
{
if(n - m + < m)
cout<<""<<endl;
else cout<<Lucas(n - m + , m, p)<<endl;
}
return ;
}
D在F介绍完之后再介绍
E:hdu-4349
求C(n,0),C (n,1),C (n,2)...C (n,n)中奇数的数目
首先:组合数奇偶性判断公式:n & m == m
当然本题要判断的组合数很多,所以不能用上述结论,只能另辟蹊径。由于是判断奇偶性,那么就是判断 是否为1,利用Lucas定理,先把和化为二进制,这样它们都是01序列了。我们又知道 。这样中为0的地方对应的中的位置只有一种可能,那就是0。
这样我们可以不用管中为0的地方,只考虑中为1的位置,可以看出,中为1的位置对应的中为0或1,其结果都是1,这样答案就是:1<<(二进制表示中1的个数)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
const int mod = 1e9 + ;
int lowbit(int x)
{
return x & (-x);
}
int main()
{
ll n, m, p;
while(cin >> n)
{
int tot = ;
while(n)
n -= lowbit(n), tot++;
cout<<(<<tot)<<endl;
}
return ;
}
F:Gym - 100633J
扩展Lucas定理模板
这里的p不是素数,用中国剩余定理合并同余方程组。
详细介绍请看全文最开始的传送门
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
const int mod = 1e9 + ;
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
ll extgcd(ll a, ll b, ll& x, ll& y)
//求解ax+by=gcd(a, b)
//返回值为gcd(a, b)
{
ll d = a;
if(b)
{
d = extgcd(b, a % b, y, x);
y -= (a / b) * x;
}
else x = , y = ;
return d;
}
ll mod_inverse(ll a, ll m)
//求解a关于模上m的逆元
//返回-1表示逆元不存在
{
ll x, y;
ll d = extgcd(a, m, x, y);
return d == ? (m + x % m) % m : -;
} ll Mul(ll n, ll pi, ll pk)//计算n! mod pk的部分值 pk为pi的ki次方
//算出的答案不包括pi的幂的那一部分
{
if(!n)return ;
ll ans = ;
if(n / pk)
{
for(ll i = ; i <= pk; i++) //求出循环节乘积
if(i % pi)ans = ans * i % pk;
ans = pow(ans, n / pk, pk); //循环节次数为n / pk
}
for(ll i = ; i <= n % pk; i++)
if(i % pi)ans = ans * i % pk;
return ans * Mul(n / pi, pi, pk) % pk;//递归求解
} ll C(ll n, ll m, ll p, ll pi, ll pk)//计算组合数C(n, m) mod pk的值 pk为pi的ki次方
{
if(m > n)return ;
ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
ll k = , ans;//k为pi的幂值
for(ll i = n; i; i /= pi)k += i / pi;
for(ll i = m; i; i /= pi)k -= i / pi;
for(ll i = n - m; i; i /= pi)k -= i / pi;
ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk;//ans就是n! mod pk的值
ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;//此时用剩余定理合并解
return ans;
} ll Lucas(ll n, ll m, ll p)
{
ll x = p;
ll ans = ;
for(ll i = ; i <= p; i++)
{
if(x % i == )
{
ll pk = ;
while(x % i == )pk *= i, x /= i;
ans = (ans + C(n, m, p, i, pk)) % p;
}
}
return ans;
} int main()
{
ll n, m, p;
while(cin >> n >> m >> p)
{
cout<<Lucas(n, m, p)<<endl;
}
return ;
}
D:HDU-4373
个for循环嵌套,有两种形式,第一类从1开始到,第二类从上一层循环当前数开始到,第一层一定是第一种类型,求总的循环的次数对364875103取余的结果。
首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把个嵌套分为几个不
同的部分,每一个部分由第一类循环开始,最终结果相乘即可。剩下的就是第二类循环的问题,假设一个
层循环,最大到,分析一下得到如下结果
(1)只有一层,则循环次数为
(2)只有前两层,则循环次数为
(3)只有前三层,则循环次数为
由此得到结论:第的循环次数为
由于364875103=97 * 3761599
用中国剩余定理合并,但是不能直接套上述模板,会超时,上述模板是对于不同的合数p分解成质数的次方,算起来很麻烦,此处364875103就等于两个质数的乘积
这里a1 = 97, a2 = 3761599所以M1 = 3761599, M2 = 97, 求出M1关于m1的逆元,M2关于m2的逆元
然后之需要求出组合数mod97的值为a1,组合数mod3761599的值为a2
ans按照上述公式就可以直接算出来了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
ll pow(ll a, ll b, ll m)
{
ll ans = ;
a %= m;
while(b)
{
if(b & )ans = (ans % m) * (a % m) % m;
b /= ;
a = (a % m) * (a % m) % m;
}
ans %= m;
return ans;
}
const ll mod1 = ;
const ll mod2 = ;
const ll mod = mod1 * mod2;
ll fac1[mod1 + ], fac2[mod2 + ];
ll inv1, inv2;
void init()
{
fac1[] = fac2[] = ;
for(int i = ; i < mod1; i++)fac1[i] = fac1[i - ] * i % mod1;
for(int i = ; i < mod2; i++)fac2[i] = fac2[i - ] * i % mod2;
inv1 = pow(mod2, mod1 - , mod1);//mod1 模上mod2的逆元
inv2 = pow(mod1, mod2 - , mod2);//mod2 模上mod1的逆元 求出在之后的中国剩余定理合并的时候不用计算
} ll C(ll n, ll m, ll p, ll fac[])
{
if(m > n)return ;
return fac[n] * pow(fac[m] * fac[n - m], p - , p) % p;
} ll Lucas(ll n, ll m, ll p, ll fac[])
{
if(m == )return ;
return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac) % p;
}
int main()
{
ll n, m, k, cases = ;
ll a[];
int T;
init();
cin >> T;
while(T--)
{
cin >> n >> m;
cin >> k;
for(int i = ; i < k; i++)cin >> a[i];
a[k] = m;
ll ans = ;
for(int i = ; i <= k; i++)
{
ll t = a[i] - a[i - ];
ll a1 = Lucas(n + t - , t, mod1, fac1);
ll a2 = Lucas(n + t - , t, mod2, fac2);
ll a3 = (a1 * mod2 % mod * inv1 + a2 * mod1 % mod * inv2) % mod;//中国剩余定理合并同余方程组
ans = ans * a3 % mod;
}
cout<<"Case #"<<++cases<<": "<<ans<<endl;
}
return ;
}
组合数取模&&Lucas定理题集的更多相关文章
- 组合数取模Lucas定理及快速幂取模
组合数取模就是求的值,根据,和的取值范围不同,采取的方法也不一样. 下面,我们来看常见的两种取值情况(m.n在64位整数型范围内) (1) , 此时较简单,在O(n2)可承受的情况下组合数的计算可以 ...
- hdu 3944 DP? 组合数取模(Lucas定理+预处理+帕斯卡公式优化)
DP? Problem Description Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0 ...
- [转]组合数取模 Lucas定理
对于C(n, m) mod p.这里的n,m,p(p为素数)都很大的情况.就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式递推了. 这里用到Lusac定理 ...
- [hdu5226]组合数求和取模(Lucas定理)
题意:给一个矩阵a,a[i][j] = C[i][j](i>=j) or 0(i < j),求(x1,y1),(x2,y2)这个子矩阵里面的所有数的和. 思路:首先问题可以转化为求(0,0 ...
- hdu 3037 费马小定理+逆元除法取模+Lucas定理
组合数学推推推最后,推得要求C(n+m,m)%p 其中n,m小于10^9,p小于1^5 用Lucas定理求(Lucas定理求nm较大时的组合数) 因为p数据较小可以直接阶乘打表求逆元 求逆元时,由费马 ...
- BZOJ-1951 古代猪文 (组合数取模Lucas+中国剩余定理+拓展欧几里得+快速幂)
数论神题了吧算是 1951: [Sdoi2010]古代猪文 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 1573 Solved: 650 [Submit ...
- lucas定理解决大组合数取模
LL MyPow(LL a, LL b) { LL ret = ; while (b) { ) ret = ret * a % MOD; a = a * a % MOD; b >>= ; ...
- 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)
J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- 大组合数取模之lucas定理模板,1<=n<=m<=1e9,1<p<=1e6,p必须为素数
typedef long long ll; /********************************** 大组合数取模之lucas定理模板,1<=n<=m<=1e9,1&l ...
随机推荐
- 使用webpack loader加载器
了解webpack请移步webpack初识! 什么是loader loaders 用于转换应用程序的资源文件,他们是运行在nodejs下的函数 使用参数来获取一个资源的来源并且返回一个新的来源(资源的 ...
- [转]flash.net.Socket
本文转自:http://designstacks.net/actionscript-3-new-capabilities http://help.adobe.com/en_US/ActionScrip ...
- golang学习之接口型函数
先说下使用接口型函数的好处: 1.不必将某个接口函数附在某个type上面,保证了命名随意 2. 可以直接调用函数或者使用该接口,两两不耽误 直接上代码吧: // interface_func proj ...
- 手把手教你用 Keras 实现 LSTM 预测英语单词发音
1. 动机 我近期在研究一个 NLP 项目,根据项目的要求,需要能够通过设计算法和模型处理单词的音节 (Syllables),并对那些没有在词典中出现的单词找到其在词典中对应的押韵词(注:这类单词类似 ...
- 二、cent OS安装配置tomcat
下载tomcat的tar包http://tomcat.apache.org/download-80.cgi 确保安装前已经安装JDKjava -version如果没有安装可以参考上一篇文章:http: ...
- LeetCode刷题第一天
1 . 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用 ...
- 撩课-Java每天5道面试题第25天
156.mvc:view-controller有什么作用? 当我们发送一个请求时,如果没有找到对应的mapping 则会对配置文件当中匹配mvc:view-controller 注意点:使用时要添加后 ...
- 从Eclipse切换到IDEA工具,哎~真香!
从Eclipse切换到IDEA工具,哎~真香!(图) 个人观点:IDEA工具用了就回不去了!!!对比很多人写,我就不赘述了.我在这里主要介绍一下IDEA工具的一些使用上的技巧,毕竟我开始学习java的 ...
- 【SSH网上商城项目实战04】EasyUI菜单的实现
转自:https://blog.csdn.net/eson_15/article/details/51297705 上一节我们使用EasyUI搭建了后台页面的框架,这一节我们主要使用EasyUI技术简 ...
- 使用装饰器减少try ...finally的重复使用
@util.try_except_bskgk def added_user_handle(cur, search_time): added_user_sql = """ ...