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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章