2019牛客暑期多校训练营(第三场)----D-Big Integer

首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/883/D
来源:牛客网
涉及:欧拉定理,快速幂

点击这里回到2019牛客暑期多校训练营解题—目录贴


题目如下
在这里插入图片描述
在这里插入图片描述
首先可以把 A(n)A(n) 公式化,即
A(n)=10n19A(n)=\frac{10^n-1}{9}

10n19=0(modp)\frac{10^n-1}{9} = 0\pmod p


1.当 99pp 互质

存在乘法逆元 inv9inv9,使得
(10n1)inv9=0(modp)(10^n-1)*inv9 = 0\pmod p
两边乘9得
10n1=0(modp)10^n-1 = 0\pmod p
变形得
10n=1(modp)10^n = 1\pmod p

1010pp 不互质,即 p=2,5p=2,5 时,明显答案为0(1111…1111是一个奇数且不是5的倍数)

if(p == 2 || p == 5){
	printf("0\n");
	continue;
}

1010pp 互质,于是变成了经典的欧拉定理的题目,可以知道使得上面式子成立的n是具有周期性的,且我们知道一个周期是 ϕ(p)\phi(p)(即100=10ϕ(p)=102ϕ(p)=1(modp)10^0 = 10^{\phi(p)} = 10^{2\phi(p)} = 1 \pmod p

但是经典欧拉定理的题目告诉我们,可能还存在最小的周期 dd,且 ϕ(p)\phi(p) 肯定是 dd 的倍数。为什么一定是倍数了,下面这个图告诉你
在这里插入图片描述
为了找到 dd 的值,只需从小到大遍历 ϕ(p)\phi(p) 的所有因数,找到某一个因数 kk 使得 10k=1(modp)10^k = 1\pmod p 成立,则 d=kd = k

根据欧拉定理可知,由于 pp 是质数,则 ϕ(p)=p1\phi(p) = p-1,故遍历 p1p-1 的所有因数。

ll qpow(ll a, ll x, ll mod){//快速幂
	ll sum = 1;
	while(x){
		if(x&1)	sum = sum * a % mod;
		a = a * a % mod;
		x >>= 1;	
	}
	return sum;
}
ll phi =p-1, d = p-1;//phi为关于p的欧拉函数,d为目前知道的最小周期
for(int i = 1; i*i <= phi; i++){
	if(phi % i == 0){//如果i是phi的因数
		ll now = phi/i;//下面是代入数据判断等式是否成立
		if(qpow(10ll, i, p) == 1)		d = min(d, 1ll*i);
		if(qpow(10ll, now, p) == 1)		d = min(d, now);
	}	
}	

得到满足等式 10n=1(modp)10^n = 1\pmod p 的最小n值(最小周期)dd 之后,由于现在的等式为
A(ij)=10ij1(modp)A(i^j) = 10^{i^j} \equiv 1\pmod p
可得只要满足 iji^jdd 的倍数,则原式成立。

dijij=kd(k)d|i^j\Leftrightarrow i^j = k*d(k为任意正整数)
如果把 dd 分解质因子得到
d=p1a1p2a2p3a3...pkakd = p_1^{a_1}p_2^{a_2}p_3^{a_3}...p_k^{a_k}ij=kd=kp1a1p2a2p3a3...pkaki^j = k*d = k*p_1^{a_1}p_2^{a_2}p_3^{a_3}...p_k^{a_k}

那么对于每一个已知 j=jxj = j_x,可以得到
d1jxixd^{\frac1{j_x}}|i_x
ii 分解质因子为
ix=kp1a1jxp2a2jxp3a3jx...pkakjxi_x = k*p_1^{\left\lceil{\frac{a_1}{j_x}}\right\rceil}p_2^{\left\lceil{\frac{a_2}{j_x}}\right\rceil}p_3^{\left\lceil{\frac{a_3}{j_x}}\right\rceil}...p_k^{\left\lceil{\frac{a_k}{j_x}}\right\rceil}

gx=p1a1jxp2a2jxp3a3jx...pkakjxg_x = p_1^{\left\lceil{\frac{a_1}{j_x}}\right\rceil}p_2^{\left\lceil{\frac{a_2}{j_x}}\right\rceil}p_3^{\left\lceil{\frac{a_3}{j_x}}\right\rceil}...p_k^{\left\lceil{\frac{a_k}{j_x}}\right\rceil}

ps为什么要向上取整捏:假设一个数 a2a^255 的因数,那么必须保证 aa 至少有 52=3\left\lceil{\frac{5}2}\right\rceil=3 个因数。但是如果 aa 只有 22 个因数的话,那么 a2a^2 最多只有 22=42^2=4 个因数。 所以要就多不就少。

求出 gxg_x 的值,那么 ixi_xk=ngxk= \left\lfloor\frac{n}{g_x}\right\rfloor 个可以取的值

即:遍历所有的 jj ,当 j=jxj=j_x,求出 gxg_x,则可得到符合条件的 iikx=ngxk_x=\left\lfloor\frac{n}{g_x}\right\rfloor个,那么所有的 kxk_x 之和,就是当 99pp 不互质时,满足条件的 (i,j)(i,j) 数对的数量。

但是 jj 的范围很广,难道要一个个遍历吗。其实 d=p1a1p2a2p3a3...pkakd = p_1^{a_1}p_2^{a_2}p_3^{a_3}...p_k^{a_k} 中的 aia_i 的值最大不超过30,那么 gx=p1a1jxp2a2jxp3a3jx...pkakjxg_x = p_1^{\left\lceil{\frac{a_1}{j_x}}\right\rceil}p_2^{\left\lceil{\frac{a_2}{j_x}}\right\rceil}p_3^{\left\lceil{\frac{a_3}{j_x}}\right\rceil}...p_k^{\left\lceil{\frac{a_k}{j_x}}\right\rceil} 中,当 jx30j_x \ge 30 时,所有的 gxg_x的值与 jx=30j_x =30 时的 gxg_x 的值相同


2.当 99pp 不互质,即 p=3p=3

如果满足 A(n)0(mod3)A(n)\equiv 0\pmod 3那么必须满足 3n3|n,即 nn 为 3 的倍数

n=ijn=i^j33 倍数。只要 ii33 的倍数,那么 iji^j 一定是 33 的倍数。

则在此情况下满足条件的数对数为 n3m\left\lfloor\frac{n}{3}\right\rfloor * m

if(p == 3){
	cout << n / 3 * m << endl;
	continue;
}

代码如下

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef long long ll;
typedef pair<ll, int> P;

vector<P> prim;//存放d的质因子及次数
int t;//题目所给变量
ll p, n, m;//题目所给变量
ll qpow(ll a, ll x, ll mod){//快速幂
	ll sum = 1;
	while(x){
		if(x&1)	sum = sum * a % mod;
		a = a * a % mod;
		x >>= 1;	
	}
	return sum;
}
void Decompose(ll pri){//分解质因数
	prim.clear();
	for(ll i = 2; i * i <= pri; i++){	
		if(pri % i == 0){
			int sum = 0;
			while(pri % i == 0){
				sum++;
				pri /= i;
			}
			prim.push_back(P(i, sum));
		}
	}
	if(pri)	prim.push_back(P(pri, 1));
	return;
}
int main(){
	cin >> t;
	while(t--){
		scanf("%lld%lld%lld", &p, &n, &m);
		if(p == 2 || p == 5){//p=2和5的情况
			printf("0\n");
			continue;
		}
		if(p == 3){//p=3的情况
			cout << n / 3 * m << endl;
			continue;
		}
		ll phi = p-1, d = p-1;//phi为关于p的欧拉函数,d为目前知道的最小周期
		for(int i = 1; i*i <= phi; i++){
			if(phi % i == 0){//如果i是phi的因数
				ll now = phi/i;//下面是代入数据判断等式是否成立
				if(qpow(10ll, i, p) == 1)		d = min(d, 1ll*i);
				if(qpow(10ll, now, p) == 1)		d = min(d, now);
			}	
		}	
		Decompose(d);//对d进行分解质因数
		ll ans = 0, g = 1;//ans为答案,g为i分解质因数的最小值
		for(int j = 1; j <= min(30ll, m); j++){//注意遍历j最多到j=30即可
			g = 1;//对g进行初始化
			for(int i = 0; i < prim.size(); i++){//获得g的值
				P p = prim[i];
				g *= qpow(p.first, ceil(1.0*p.second/j), 1e9+7);
			}	
			ans += (ll)n/g;//把所有的k加起来
		}
		if(m > 30)	ans += (ll)(m - 30) * (ll)(n / g);//最后加上j大于30的所有情况。
		printf("%lld\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章