组合数取模
2015-07-15 16:13
399 查看
组合数取模
分类:
数论 2012-10-03 12:41
4816人阅读 评论(6)
收藏
举报
组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。
组合数取模就是求
的值,当然根据
,
和
的取值范围不同,采取的方法也不一样。
接下来,我们来学习一些常见的取值情况
(1)
和
这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于
和
的范围小,直接两层循环即可。
(2)
和
,并且
是素数
这个问题有个叫做Lucas的定理,定理描述是,如果
那么得到
这样然后分别求,采用逆元计算即可。
题目:http://acm.fzu.edu.cn/problem.php?pid=2020
题意:求
,其中
,并且
是素数。
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
LL n,m,p;
LL quick_mod(LL a, LL b)
{
LL ans = 1;
a %= p;
while(b)
{
if(b & 1)
{
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
LL C(LL n, LL m)
{
if(m > n) return 0;
LL ans = 1;
for(int i=1; i<=m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans = ans * (a * quick_mod(b, p-2) % p) % p;
}
return ans;
}
LL Lucas(LL n, LL m)
{
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%I64d%I64d%I64d", &n, &m, &p);
printf("%I64d\n", Lucas(n,m));
}
return 0;
}
由于上题的
比较大,所以组合数只能一个一个计算,如果
的范围小点,那么就可以进行阶乘预处理计算了。
(3)
和
,并且
可能为合数
这样的话先采取暴力分解,然后快速幂即可。
题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=628
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 200005;
bool prime
;
int p
;
int cnt;
void isprime()
{
cnt = 0;
memset(prime,true,sizeof(prime));
for(int i=2; i<N; i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL Work(LL n,LL p)
{
LL ans = 0;
while(n)
{
ans += n / p;
n /= p;
}
return ans;
}
LL Solve(LL n,LL m,LL P)
{
LL ans = 1;
for(int i=0; i<cnt && p[i]<=n; i++)
{
LL x = Work(n, p[i]);
LL y = Work(n - m, p[i]);
LL z = Work(m, p[i]);
x -= (y + z);
ans *= quick_mod(p[i],x,P);
ans %= P;
}
return ans;
}
int main()
{
int T;
isprime();
cin>>T;
while(T--)
{
LL n,m,P;
cin>>n>>m>>P;
n += m - 2;
m--;
cout<<Solve(n,m,P)<<endl;
}
return 0;
}
接下来看一些关于组合数取模的典型题目。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3944
分析:组合数取模的典型题目,用Lucas定理,注意要阶乘预处理,否则会TLE的。
题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4536
题意:给一个集合,一共
个元素,从中选取
个元素,选出的元素中没有相邻的元素的选法一共有多少种?
分析:典型的隔板法,最终答案就是
。然后用Lucas定理处理即可。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4373
题意:
个for循环嵌套,有两种形式,第一类从1开始到
,第二类从上一层循环当前数开始到
,第一层一定
是第一种类型,求总的循环的次数对364875103取余的结果。
分析:首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把
个嵌套分为几个不
同的部分,每一个部分由第一类循环开始,最终结果相乘即可。剩下的就是第二类循环的问题,假设一个
层循环,最大到
,分析一下得到如下结果
(1)只有一层,则循环次数为
(2)只有前两层,则循环次数为
(3)只有前三层,则循环次数为
由此得到结论:第
的循环次数为
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 25;
const int MOD1 = 97;
const int MOD2 = 3761599;
const int MOD = MOD1 * MOD2;
int m,n,k;
int a
;
LL fac1[MOD1+10];
LL fac2[MOD2+10];
LL inv1,inv2;
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL C(LL n,LL m,LL p,LL fac[])
{
if(n < m) return 0;
return fac
* quick_mod(fac[m] * fac[n-m], p - 2, p) % p;
}
LL Lucas(LL n,LL m,LL p,LL fac[])
{
if(m == 0) return 1;
return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac);
}
void Init()
{
fac1[0] = fac2[0] = 1;
for(int i=1; i<MOD1; i++)
fac1[i] = (fac1[i-1] * i) % MOD1;
for(int i=1; i<MOD2; i++)
fac2[i] = (fac2[i-1] * i) % MOD2;
inv1 = MOD2 * quick_mod(MOD2, MOD1-2, MOD1);
inv2 = MOD1 * quick_mod(MOD1, MOD2-2, MOD2);
}
int main()
{
Init();
int T, tt = 1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0; i<k; i++)
scanf("%d",&a[i]);
a[k] = m;
LL ans = 1;
for(int i=0; i<k; i++)
{
LL m1 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD1, fac1);
LL m2 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD2, fac2);
LL mm = (m1 * inv1 + m2 * inv2) % MOD;
ans = ans * mm % MOD;
}
printf("Case #%d: ",tt++);
cout<<ans<<endl;
}
return 0;
}
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4349
题意:
中有多少个奇数,其中
。
分析:其实组合数判断奇偶性有一个优美的结论
如果
,那么
为奇数,否则为偶数
当然本题要判断的组合数很多,所以不能用上述结论,只能另辟蹊径。由于是判断奇偶性,那么就是判断
是否为1,利用Lucas定理,先把
和
化为二进制,这样它们都是01序列了。我们又知道
。这样
中为0的地方对应的
中的位置只有一种可能,那就是0。
这样我们可以不用管
中为0的地方,只考虑
中为1的位置,可以看出,
中为1的位置对应的
中为0
或1,其结果都是1,这样答案就是:1<<(
二进制表示中1的个数)
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int cnt = 0;
while (n)
{
if (n & 1) cnt++;
n >>= 1;
}
printf("%d\n",1<<cnt);
}
return 0;
}
题目:http://61.187.179.132/JudgeOnline/problem.php?id=1951
题意:给定两个正整数
和
,其中
,求下面表达式的值
分析:由于999911659是素数,用费马小定理降幂得到
现在关键是求
那么我们枚举
分别计算,但是模的是合数,所以对999911658进行分解得到
,那么分别求
,即
然后进一步得到同余方程组为
再通过中国剩余定理(CRT)可以求得最终答案
。
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int P = 999911659;
LL a[5] = {0, 0, 0, 0};
LL m[5] = {2, 3, 4679, 35617};
LL fac[5][36010];
LL N, G;
void Init()
{
for(int i=0; i<4; i++)
{
fac[i][0] = 1;
for(int j=1; j<36010; j++)
fac[i][j] = fac[i][j-1] * j % m[i];
}
}
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL C(LL n,LL k,int cur)
{
LL p = m[cur];
if(k > n) return 0;
return fac[cur]
* quick_mod(fac[cur][k] * fac[cur][n-k], p - 2, p) % p;
}
LL Lucas(LL n,LL k,int cur)
{
LL p = m[cur];
if(k == 0) return 1;
return C(n % p, k % p, cur) * Lucas(n / p, k / p, cur) % p;
}
void extend_Euclid(LL a, LL b, LL &x, LL &y)
{
if(b == 0)
{
x = 1;
y = 0;
return;
}
extend_Euclid(b, a % b,x, y);
LL tmp = x;
x = y;
y = tmp - a / b * y;
}
LL RemindChina(LL a[],LL m[],int k)
{
LL M = 1;
LL ans = 0;
for(int i=0; i<k; i++)
M *= m[i];
for(int i=0; i<k; i++)
{
LL x, y;
LL Mi = M / m[i];
extend_Euclid(Mi, m[i], x, y);
ans = (ans + Mi * x * a[i]) % M;
}
if(ans < 0)
ans += M;
return ans;
}
int main()
{
Init();
while(cin>>N>>G)
{
a[0] = a[1] = 0;
a[2] = a[3] = 0;
if(G == P)
{
cout<<"0"<<endl;
continue;
}
G %= P;
for(int i=1; i*i <= N; i++)
{
if(N % i == 0)
{
LL x = i;
a[0] = (a[0] + Lucas(N, x, 0)) % m[0];
a[1] = (a[1] + Lucas(N, x, 1)) % m[1];
a[2] = (a[2] + Lucas(N, x, 2)) % m[2];
a[3] = (a[3] + Lucas(N, x, 3)) % m[3];
x = N / i;
if(i * i != N)
{
a[0] = (a[0] + Lucas(N, x, 0)) % m[0];
a[1] = (a[1] + Lucas(N, x, 1)) % m[1];
a[2] = (a[2] + Lucas(N, x, 2)) % m[2];
a[3] = (a[3] + Lucas(N, x, 3)) % m[3];
}
}
}
LL ans = quick_mod(G, RemindChina(a, m, 4), P);
cout<<ans<<endl;
}
return 0;
}
题目:已知有如下表达式
给定
和
,求
。
分析:如果直接二项式展开,这样会很麻烦,而且不容易求出,本题有技巧。做如下变换
所以问题变为求
的值。
组合数取模
分类: 数论 2012-10-03 12:41
4816人阅读 评论(6)
收藏
举报
组合数取模在ACM竞赛中是一个很重要的问题,很多选手因为数据太大而束手无策,今天就来详细讲解它。
组合数取模就是求
的值,当然根据
,
和
的取值范围不同,采取的方法也不一样。
接下来,我们来学习一些常见的取值情况
(1)
和
这个问题比较简单,组合数的计算可以靠杨辉三角,那么由于
和
的范围小,直接两层循环即可。
(2)
和
,并且
是素数
这个问题有个叫做Lucas的定理,定理描述是,如果
那么得到
这样然后分别求,采用逆元计算即可。
题目:http://acm.fzu.edu.cn/problem.php?pid=2020
题意:求
,其中
,并且
是素数。
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
LL n,m,p;
LL quick_mod(LL a, LL b)
{
LL ans = 1;
a %= p;
while(b)
{
if(b & 1)
{
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
LL C(LL n, LL m)
{
if(m > n) return 0;
LL ans = 1;
for(int i=1; i<=m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans = ans * (a * quick_mod(b, p-2) % p) % p;
}
return ans;
}
LL Lucas(LL n, LL m)
{
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%I64d%I64d%I64d", &n, &m, &p);
printf("%I64d\n", Lucas(n,m));
}
return 0;
}
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; typedef long long LL; LL n,m,p; LL quick_mod(LL a, LL b) { LL ans = 1; a %= p; while(b) { if(b & 1) { ans = ans * a % p; b--; } b >>= 1; a = a * a % p; } return ans; } LL C(LL n, LL m) { if(m > n) return 0; LL ans = 1; for(int i=1; i<=m; i++) { LL a = (n + i - m) % p; LL b = i % p; ans = ans * (a * quick_mod(b, p-2) % p) % p; } return ans; } LL Lucas(LL n, LL m) { if(m == 0) return 1; return C(n % p, m % p) * Lucas(n / p, m / p) % p; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%I64d%I64d%I64d", &n, &m, &p); printf("%I64d\n", Lucas(n,m)); } return 0; }
由于上题的
比较大,所以组合数只能一个一个计算,如果
的范围小点,那么就可以进行阶乘预处理计算了。
(3)
和
,并且
可能为合数
这样的话先采取暴力分解,然后快速幂即可。
题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=628
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 200005;
bool prime
;
int p
;
int cnt;
void isprime()
{
cnt = 0;
memset(prime,true,sizeof(prime));
for(int i=2; i<N; i++)
{
if(prime[i])
{
p[cnt++] = i;
for(int j=i+i; j<N; j+=i)
prime[j] = false;
}
}
}
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL Work(LL n,LL p)
{
LL ans = 0;
while(n)
{
ans += n / p;
n /= p;
}
return ans;
}
LL Solve(LL n,LL m,LL P)
{
LL ans = 1;
for(int i=0; i<cnt && p[i]<=n; i++)
{
LL x = Work(n, p[i]);
LL y = Work(n - m, p[i]);
LL z = Work(m, p[i]);
x -= (y + z);
ans *= quick_mod(p[i],x,P);
ans %= P;
}
return ans;
}
int main()
{
int T;
isprime();
cin>>T;
while(T--)
{
LL n,m,P;
cin>>n>>m>>P;
n += m - 2;
m--;
cout<<Solve(n,m,P)<<endl;
}
return 0;
}
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; typedef long long LL; const int N = 200005; bool prime ; int p ; int cnt; void isprime() { cnt = 0; memset(prime,true,sizeof(prime)); for(int i=2; i<N; i++) { if(prime[i]) { p[cnt++] = i; for(int j=i+i; j<N; j+=i) prime[j] = false; } } } LL quick_mod(LL a,LL b,LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } LL Work(LL n,LL p) { LL ans = 0; while(n) { ans += n / p; n /= p; } return ans; } LL Solve(LL n,LL m,LL P) { LL ans = 1; for(int i=0; i<cnt && p[i]<=n; i++) { LL x = Work(n, p[i]); LL y = Work(n - m, p[i]); LL z = Work(m, p[i]); x -= (y + z); ans *= quick_mod(p[i],x,P); ans %= P; } return ans; } int main() { int T; isprime(); cin>>T; while(T--) { LL n,m,P; cin>>n>>m>>P; n += m - 2; m--; cout<<Solve(n,m,P)<<endl; } return 0; }
接下来看一些关于组合数取模的典型题目。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3944
分析:组合数取模的典型题目,用Lucas定理,注意要阶乘预处理,否则会TLE的。
题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4536
题意:给一个集合,一共
个元素,从中选取
个元素,选出的元素中没有相邻的元素的选法一共有多少种?
分析:典型的隔板法,最终答案就是
。然后用Lucas定理处理即可。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4373
题意:
个for循环嵌套,有两种形式,第一类从1开始到
,第二类从上一层循环当前数开始到
,第一层一定
是第一种类型,求总的循环的次数对364875103取余的结果。
分析:首先可以看出,每一个第一类循环都是一个新的开始,与前面的状态无关,所以可以把
个嵌套分为几个不
同的部分,每一个部分由第一类循环开始,最终结果相乘即可。剩下的就是第二类循环的问题,假设一个
层循环,最大到
,分析一下得到如下结果
(1)只有一层,则循环次数为
(2)只有前两层,则循环次数为
(3)只有前三层,则循环次数为
由此得到结论:第
的循环次数为
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int N = 25;
const int MOD1 = 97;
const int MOD2 = 3761599;
const int MOD = MOD1 * MOD2;
int m,n,k;
int a
;
LL fac1[MOD1+10];
LL fac2[MOD2+10];
LL inv1,inv2;
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL C(LL n,LL m,LL p,LL fac[])
{
if(n < m) return 0;
return fac
* quick_mod(fac[m] * fac[n-m], p - 2, p) % p;
}
LL Lucas(LL n,LL m,LL p,LL fac[])
{
if(m == 0) return 1;
return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac);
}
void Init()
{
fac1[0] = fac2[0] = 1;
for(int i=1; i<MOD1; i++)
fac1[i] = (fac1[i-1] * i) % MOD1;
for(int i=1; i<MOD2; i++)
fac2[i] = (fac2[i-1] * i) % MOD2;
inv1 = MOD2 * quick_mod(MOD2, MOD1-2, MOD1);
inv2 = MOD1 * quick_mod(MOD1, MOD2-2, MOD2);
}
int main()
{
Init();
int T, tt = 1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0; i<k; i++)
scanf("%d",&a[i]);
a[k] = m;
LL ans = 1;
for(int i=0; i<k; i++)
{
LL m1 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD1, fac1);
LL m2 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD2, fac2);
LL mm = (m1 * inv1 + m2 * inv2) % MOD;
ans = ans * mm % MOD;
}
printf("Case #%d: ",tt++);
cout<<ans<<endl;
}
return 0;
}
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; typedef long long LL; const int N = 25; const int MOD1 = 97; const int MOD2 = 3761599; const int MOD = MOD1 * MOD2; int m,n,k; int a ; LL fac1[MOD1+10]; LL fac2[MOD2+10]; LL inv1,inv2; LL quick_mod(LL a,LL b,LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } LL C(LL n,LL m,LL p,LL fac[]) { if(n < m) return 0; return fac * quick_mod(fac[m] * fac[n-m], p - 2, p) % p; } LL Lucas(LL n,LL m,LL p,LL fac[]) { if(m == 0) return 1; return C(n % p, m % p, p, fac) * Lucas(n / p, m / p, p, fac); } void Init() { fac1[0] = fac2[0] = 1; for(int i=1; i<MOD1; i++) fac1[i] = (fac1[i-1] * i) % MOD1; for(int i=1; i<MOD2; i++) fac2[i] = (fac2[i-1] * i) % MOD2; inv1 = MOD2 * quick_mod(MOD2, MOD1-2, MOD1); inv2 = MOD1 * quick_mod(MOD1, MOD2-2, MOD2); } int main() { Init(); int T, tt = 1; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); for(int i=0; i<k; i++) scanf("%d",&a[i]); a[k] = m; LL ans = 1; for(int i=0; i<k; i++) { LL m1 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD1, fac1); LL m2 = Lucas(a[i+1] - a[i] + n - 1, a[i+1] - a[i], MOD2, fac2); LL mm = (m1 * inv1 + m2 * inv2) % MOD; ans = ans * mm % MOD; } printf("Case #%d: ",tt++); cout<<ans<<endl; } return 0; }
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4349
题意:
中有多少个奇数,其中
。
分析:其实组合数判断奇偶性有一个优美的结论
如果
,那么
为奇数,否则为偶数
当然本题要判断的组合数很多,所以不能用上述结论,只能另辟蹊径。由于是判断奇偶性,那么就是判断
是否为1,利用Lucas定理,先把
和
化为二进制,这样它们都是01序列了。我们又知道
。这样
中为0的地方对应的
中的位置只有一种可能,那就是0。
这样我们可以不用管
中为0的地方,只考虑
中为1的位置,可以看出,
中为1的位置对应的
中为0
或1,其结果都是1,这样答案就是:1<<(
二进制表示中1的个数)
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int cnt = 0;
while (n)
{
if (n & 1) cnt++;
n >>= 1;
}
printf("%d\n",1<<cnt);
}
return 0;
}
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; int main() { int n; while(scanf("%d",&n)!=EOF) { int cnt = 0; while (n) { if (n & 1) cnt++; n >>= 1; } printf("%d\n",1<<cnt); } return 0; }
题目:http://61.187.179.132/JudgeOnline/problem.php?id=1951
题意:给定两个正整数
和
,其中
,求下面表达式的值
分析:由于999911659是素数,用费马小定理降幂得到
现在关键是求
那么我们枚举
分别计算,但是模的是合数,所以对999911658进行分解得到
,那么分别求
,即
然后进一步得到同余方程组为
再通过中国剩余定理(CRT)可以求得最终答案
。
代码:
[cpp]
view plaincopyprint?
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int P = 999911659;
LL a[5] = {0, 0, 0, 0};
LL m[5] = {2, 3, 4679, 35617};
LL fac[5][36010];
LL N, G;
void Init()
{
for(int i=0; i<4; i++)
{
fac[i][0] = 1;
for(int j=1; j<36010; j++)
fac[i][j] = fac[i][j-1] * j % m[i];
}
}
LL quick_mod(LL a,LL b,LL m)
{
LL ans = 1;
a %= m;
while(b)
{
if(b & 1)
{
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
LL C(LL n,LL k,int cur)
{
LL p = m[cur];
if(k > n) return 0;
return fac[cur]
* quick_mod(fac[cur][k] * fac[cur][n-k], p - 2, p) % p;
}
LL Lucas(LL n,LL k,int cur)
{
LL p = m[cur];
if(k == 0) return 1;
return C(n % p, k % p, cur) * Lucas(n / p, k / p, cur) % p;
}
void extend_Euclid(LL a, LL b, LL &x, LL &y)
{
if(b == 0)
{
x = 1;
y = 0;
return;
}
extend_Euclid(b, a % b,x, y);
LL tmp = x;
x = y;
y = tmp - a / b * y;
}
LL RemindChina(LL a[],LL m[],int k)
{
LL M = 1;
LL ans = 0;
for(int i=0; i<k; i++)
M *= m[i];
for(int i=0; i<k; i++)
{
LL x, y;
LL Mi = M / m[i];
extend_Euclid(Mi, m[i], x, y);
ans = (ans + Mi * x * a[i]) % M;
}
if(ans < 0)
ans += M;
return ans;
}
int main()
{
Init();
while(cin>>N>>G)
{
a[0] = a[1] = 0;
a[2] = a[3] = 0;
if(G == P)
{
cout<<"0"<<endl;
continue;
}
G %= P;
for(int i=1; i*i <= N; i++)
{
if(N % i == 0)
{
LL x = i;
a[0] = (a[0] + Lucas(N, x, 0)) % m[0];
a[1] = (a[1] + Lucas(N, x, 1)) % m[1];
a[2] = (a[2] + Lucas(N, x, 2)) % m[2];
a[3] = (a[3] + Lucas(N, x, 3)) % m[3];
x = N / i;
if(i * i != N)
{
a[0] = (a[0] + Lucas(N, x, 0)) % m[0];
a[1] = (a[1] + Lucas(N, x, 1)) % m[1];
a[2] = (a[2] + Lucas(N, x, 2)) % m[2];
a[3] = (a[3] + Lucas(N, x, 3)) % m[3];
}
}
}
LL ans = quick_mod(G, RemindChina(a, m, 4), P);
cout<<ans<<endl;
}
return 0;
}
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; typedef long long LL; const int P = 999911659; LL a[5] = {0, 0, 0, 0}; LL m[5] = {2, 3, 4679, 35617}; LL fac[5][36010]; LL N, G; void Init() { for(int i=0; i<4; i++) { fac[i][0] = 1; for(int j=1; j<36010; j++) fac[i][j] = fac[i][j-1] * j % m[i]; } } LL quick_mod(LL a,LL b,LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } LL C(LL n,LL k,int cur) { LL p = m[cur]; if(k > n) return 0; return fac[cur] * quick_mod(fac[cur][k] * fac[cur][n-k], p - 2, p) % p; } LL Lucas(LL n,LL k,int cur) { LL p = m[cur]; if(k == 0) return 1; return C(n % p, k % p, cur) * Lucas(n / p, k / p, cur) % p; } void extend_Euclid(LL a, LL b, LL &x, LL &y) { if(b == 0) { x = 1; y = 0; return; } extend_Euclid(b, a % b,x, y); LL tmp = x; x = y; y = tmp - a / b * y; } LL RemindChina(LL a[],LL m[],int k) { LL M = 1; LL ans = 0; for(int i=0; i<k; i++) M *= m[i]; for(int i=0; i<k; i++) { LL x, y; LL Mi = M / m[i]; extend_Euclid(Mi, m[i], x, y); ans = (ans + Mi * x * a[i]) % M; } if(ans < 0) ans += M; return ans; } int main() { Init(); while(cin>>N>>G) { a[0] = a[1] = 0; a[2] = a[3] = 0; if(G == P) { cout<<"0"<<endl; continue; } G %= P; for(int i=1; i*i <= N; i++) { if(N % i == 0) { LL x = i; a[0] = (a[0] + Lucas(N, x, 0)) % m[0]; a[1] = (a[1] + Lucas(N, x, 1)) % m[1]; a[2] = (a[2] + Lucas(N, x, 2)) % m[2]; a[3] = (a[3] + Lucas(N, x, 3)) % m[3]; x = N / i; if(i * i != N) { a[0] = (a[0] + Lucas(N, x, 0)) % m[0]; a[1] = (a[1] + Lucas(N, x, 1)) % m[1]; a[2] = (a[2] + Lucas(N, x, 2)) % m[2]; a[3] = (a[3] + Lucas(N, x, 3)) % m[3]; } } } LL ans = quick_mod(G, RemindChina(a, m, 4), P); cout<<ans<<endl; } return 0; }
题目:已知有如下表达式
给定
和
,求
。
分析:如果直接二项式展开,这样会很麻烦,而且不容易求出,本题有技巧。做如下变换
所以问题变为求
的值。
相关文章推荐
- UI之UILable详解
- 第十一章:关联容器
- 使用sshfs挂载时自动输入密码
- Map.get方法——返回指定键所映射的值
- 第四个selenium-radiobutton
- java基础之 创建对象的几种方式
- charles使用教程指南
- 关于listview 多项删除
- java高保真高性能图片缩放编码
- java高保真高性能图片缩放编码
- 探秘TCP数据包中的PSH标志
- GPU并行编程方法
- 输入法相关问题记录
- 设置TextView文字居中
- 学习笔记:Twitter核心数据类库团队的Hadoop优化经验
- LayoutInflater的inflate函数用法详解
- 11G新特性 -- 块介质恢复性能增强(block media recovery)
- leetcode 035 —— Search Insert Position
- 一次非常有意思的sql优化经历
- 自定义标签拦截及实现