P2480 [SDOI2010]古代猪文

题目描述

猪王国的文明源远流长,博大精深。

iPig 在大肥猪学校图书馆中查阅资料,得知远古时期猪文文字总个数为 \(n\) 。当然,一种语言如果字数很多,字典也相应会很大。

当时的猪王国国王考虑到如果修一本字典,规模有可能远远超过康熙字典,花费的猪力、物力将难以估量。故考虑再三没有进行这一项劳猪伤财之举

。当然,猪王国的文字后来随着历史变迁逐渐进行了简化,去掉了一些不常用的字。

iPig 打算研究古时某个朝代的猪文文字。根据相关文献记载,那个朝代流传的猪文文字恰好为远古时期的 \(1\over k\)

其中 \(k\)\(n\) 的一个正约数(可以是 \(1\)\(n\))。不过具体是哪 \(1\over k\),以及 \(k\) 是多少,由于历史过于久远,已经无从考证了。

iPig 觉得只要符合文献,每一种 \(k∣n\) 都是有可能的。他打算考虑到所有可能的 \(k\)。显然当 \(k\) 等于某个定值时,

该朝的猪文文字个数为 \(n \over k\)。然而从 \(n\) 个文字中保留下 \(n\over k\) 个的情况也是相当多的。iPig 预计,

如果所有可能的 \(k\) 的所有情况数加起来为 \(p\) 的话,那么他研究古代文字的代价将会是 \(g^p\)

现在他想知道猪王国研究古代文字的代价是多少。由于 iPig 觉得这个数字可能是天文数字,

所以你只需要告诉他答案除以 \(999911659\) 的余数就可以了。

输入格式

一行两个正整数 n,g。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 #1

4 2

输出 #1

2048

数据范围与提示

对于 \(10\%\) 的数据 \(1\le n \le 50\)
对于 \(20\%\) 的数据,\(1\le n \le 1000\)
对于 \(40\%\) 的数据,\(1\le n \le 10^5\)
对于 \(100\%\) 的数据,\(1\le n,g \le 10^9\)

前置芝士

  1. 中国剩余定理(求答案)

  2. 欧拉定理拓展

  3. Lucas定理(求组合数)

一句话题意 \(g^{\sum_{d|n} C_{n}^{d} \bmod 999911659}\)

首先,根据欧拉定理的拓展可得

\(g^{\sum_{d|n} C_{n}^{d}} \bmod 999911659\) = \(g^{\sum_{d|n}C_{n}^{d} \bmod 999911658}\)

这个题关键就在于求出\(\sum_{d|n}C_{n}^{d} \bmod 999911658\)这个柿子

看到n的范围那么大,只有卢卡斯定理可以解决这种问题。

但模数又不是质数,那怎么办呢?

我们可以考虑对 \(999911658\) 进行因数分解 \(999911658 = 2 \times 3 \times 4679 \times 35617\)

发现他的因数都是质数,且指数都为1.

那这么不就可以用Lucas定理求出组合数了吗?

最后,我们考虑怎么统计答案

假设,我们计算出\(\sum_{d|n}C_{n}{d}\) mod 2,3,4679,35617 的结果分别为s1,s2,s3,s4

那么我们可以得到这样一组线性方程

\[\left\{ \begin{array}{lrc} x \equiv s1 (\bmod 2) \\ x \equiv s2 (\bmod 3) \\ x \equiv s3 (\bmod 4679) \\ x \equiv s4 (\bmod 35617) \end{array} \right. \]

这样,我们就可以利用中国剩余定理求解出最小的解,然后快速幂算出答案就解决了。

除此之外,还要特判一下 g = 999911659 的情况

为毛关于猪的都这么毒瘤(猪国杀,魔法猪学院,古代猪文

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define int long long
int mod = 999911658;
int g,n,ans,jz[36000],sum[5];
int p[5] = {0,2,3,4679,35617};
LL ksm(LL a,LL b,LL p)
{
	LL res = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a % p;
		a = a * a % p;
	}
	return res;
}
LL C(int n,int m,int p)//计算组合数
{
	if(n < m) return 0;
	return jz[n] % p * ksm(jz[m],p-2,p) % p * ksm(jz[n-m],p-2,p) % p;
}
LL Lucas(int n,int m,int p)//Lucas定理求组合数
{
	if(m == 0) return 1;
	return C(n%p,m%p,p) * Lucas(n/p,m/p,p) % p;	
}
signed main()
{
	scanf("%d%d",&n,&g); jz[0] = 1;
	if(g == 999911659)//特判一下
	{
		cout<<0<<endl;
		return 0;
	}
	for(int i = 1; i <= 36000; i++) jz[i] = jz[i-1] * i % mod;//预处理出阶乘
	for(int i = 1; i * i <= n; i++)//枚举因子
	{
		for(int j = 1; j <= 4; j++)
		{
			if(i * i == n)
			{
				sum[j] = (sum[j] + Lucas(n,i,p[j])) % p[j];//计算出s1,s2,s3,s4
			}
			else if(n % i == 0)
			{
				sum[j] = (sum[j] + Lucas(n,i,p[j])) % p[j];
				sum[j] = (sum[j] + Lucas(n,n/i,p[j])) % p[j];
			}
		}
	}
	for(int i = 1; i <= 4; i++)
	{
		ans = (ans + sum[i] * (mod/p[i]) % mod * ksm(mod/p[i],p[i]-2,p[i]) % mod) % mod;//中国剩余定理求解线性同余方程
	}
	ans = (ans % mod + mod) % mod; mod++;
	printf("%lld\n",ksm(g,ans,mod));
}  

ENDING

posted @ 2020-08-12 19:18  genshy  阅读(152)  评论(0编辑  收藏  举报