bzoj1951: [Sdoi2010]古代猪文(数学)

bzoj1951
题目描述:给定n和g,求\(g^{\sum_{i = 1}^n i|n C_{n}^{i}}\) mod 999911659

输入格式:一行两个数,分别表示n和g

输出格式:一行一个整数,表示答案mod 999911659后的结果

输入样例:
4 2

输出样例:
2048

解析:直接计算组合数肯定不行,那要怎么办呢?
   根据欧拉定理的推论可知,\(g^{\sum_{i = 1}^n i|n C_{n}^{i}}\) \(\equiv\) \(g^{\sum_{i = 1}^n i|n C_{n}^{i}mod 999911658}\) mod 999911659
   而999911658 = 2 \(\times\) 3 \(\times\) 4679 \(\times\) 35617,这样就将模数缩小,可以直接用lucas定理进行计算
   算出结果对4个模数的结果后,再用中国剩余定理将结果合并即可。

代码如下:

#include<cstdio>
using namespace std;

const int MOD = 999911659;
const int b[] = {2, 3, 4679, 35617};
int g, n, fac[40005], ans, a[5];

int ksm(int x, int y, int p) { //快速幂 
	int res = 1, base = x;
	  while (y) {
	  	if (y & 1) res = 1ll * res * base % p;
	  	base = 1ll * base * base % p;
	  	y >>= 1;
	  }
	return res;
}

int C(int x, int y, int p) { //算组合数 
	if (y > x) return 0;
	int inv = 1ll * ksm(fac[y], p - 2, p) * ksm(fac[x - y], p - 2, p) % p;
	return 1ll * fac[x] * inv % p;
}

int lucas(int x, int y, int p) { //lucas定理 
	if (y > x) return 0; 
	if (!x) return 1;
	return 1ll * lucas(x / p, y / p, p) * C(x % p, y % p, p) % p;
}

void crt(void) { //中国剩余定理 
	for (int i = 0; i < 4; ++ i) {
	  ans += 1ll * a[i] * ((MOD - 1) / b[i]) % (MOD - 1) * ksm((MOD - 1) / b[i], b[i] - 2, b[i]) % (MOD - 1);
	  ans %= MOD - 1;
	}
}

int main() { 
	scanf("%d %d", &n, &g);
	fac[0] = 1;
	  if (g % MOD == 0) return printf("0"),0;
	  for (int j = 0; j < 4; ++ j) { //枚举4个模数计算答案 
	  	for (int i = 1; i <= b[j]; ++ i) fac[i] = 1ll * fac[i - 1] * i % b[j];
	  	for (int i = 1; 1ll * i * i <= n; ++ i) {
	  	    if (n % i) continue;
	  	    a[j] = (a[j] + lucas(n, i, b[j])) % b[j];
	  	    if (n / i != i) a[j] = (a[j] + lucas(n, n / i, b[j])) % b[j];
	      }
	  }
	crt();
	printf("%d", ksm(g, ans, MOD));
	return 0;
}
posted @ 2018-11-09 10:03  Gax_c  阅读(130)  评论(0编辑  收藏  举报