您的位置:首页 > 其它

【bzoj1951】[Sdoi2010]古代猪文 费马小定理+Lucas定理+中国剩余定理

2017-10-13 14:48 387 查看
题目描述

$g^{\sum\limits_{k|n}C_{n}^{\frac nk}}\mod 999911659$

输入

有且仅有一行:两个数N、G,用一个空格分开。

输出

有且仅有一行:一个数,表示答案除以999911659的余数。

样例输入

4 2

样例输出

2048

题解

费马小定理+Lucas定理+中国剩余定理

首先由费马小定理$a^{p-1}\equiv 1\ \ (mod\ p)$,可以将模数转化到答案的指数上,即求$\sum\limits_{k|n}C_{n}^{\frac nk}\ mod\ 999911658\ =\ \sum\limits_{k|n}C_{n}^{k}\ mod\ 999911658$。

然后直接$O(\sqrt n)$枚举一对约数中较小的那个,直接计算即可。

但这里有一个问题:减1以后模数不是质数。

于是我们需要将其进行质因子分解:$999911658=2*3*4679*35617$,然后先使用Lucas定理算出$C_n^k$在模其质因子意义下的结果,然后再使用中国剩余定理合并模线性同余方程组即可。在求解时,由于一定有解,因此不需要判定解得合法性,直接求逆元除过去即可。

时间复杂度$O(\sqrt{n}\log^2n)$

注意本题的一个巨大坑点:当$a\ mod\ p=0$时费马小定理不成立(底数模意义下为0,指数不为0但模意义下为0时会计算为1,而实际上0的正整数次幂应该为0),因此需要特判这种情况。

#include <cstdio>
#define MOD 999911658
typedef long long ll;
const int mod[] = {2 , 3 , 4679 , 35617};
ll fac[4][36000];
inline ll pow(ll x , ll y , int mod)
{
ll ans = 1;
while(y)
{
if(y & 1) ans = ans * x % mod;
x = x * x % mod , y >>= 1;
}
return ans;
}
ll calc(int n , int m , int p)
{
if(n < m) return 0;
if(n < mod[p]) return fac[p]
* pow(fac[p][m] , mod[p] - 2 , mod[p]) * pow(fac[p][n - m] , mod[p] - 2 , mod[p]) % mod[p];
return calc(n / mod[p] , m / mod[p] , p) * calc(n % mod[p] , m % mod[p] , p) % mod[p];
}
ll C(int n , int m)
{
int i;
ll ans = 0;
for(i = 0 ; i < 4 ; i ++ )
ans = (ans + MOD / mod[i] * pow(MOD / mod[i] , mod[i] - 2 , mod[i]) % MOD * calc(n , m , i)) % MOD;
return ans;
}
int main()
{
int n , g , i , j;
ll sum = 0;
scanf("%d%d" , &n , &g) , g %= MOD + 1;
if(!g)
{
puts("0");
return 0;
}
for(i = 0 ; i < 4 ; i ++ )
{
fac[i][0] = 1;
for(j = 1 ; j < mod[i] ; j ++ ) fac[i][j] = fac[i][j - 1] * j % mod[i];
}
for(i = 1 ; i * i <= n ; i ++ )
{
if(n % i == 0)
{
sum = (sum + C(n , i)) % MOD;
if(i * i != n) sum = (sum + C(n , n / i)) % MOD;
}
}
printf("%lld\n" , pow(g , sum , MOD + 1));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐