首先發出題目鏈接:
鏈接:https://ac.nowcoder.com/acm/contest/883/D
來源:牛客網
涉及:歐拉定理,快速冪
點擊這裏回到2019牛客暑期多校訓練營解題—目錄貼
題目如下
首先可以把 公式化,即
則
1.當 與 互質
則存在乘法逆元 ,使得
兩邊乘9得
變形得
當 與 不互質,即 時,明顯答案爲0(1111…1111是一個奇數且不是5的倍數)
if(p == 2 || p == 5){
printf("0\n");
continue;
}
當 與 互質,於是變成了經典的歐拉定理的題目,可以知道使得上面式子成立的n是具有周期性的,且我們知道一個週期是 (即)
但是經典歐拉定理的題目告訴我們,可能還存在最小的週期 ,且 肯定是 的倍數。爲什麼一定是倍數了,下面這個圖告訴你
爲了找到 的值,只需從小到大遍歷 的所有因數,找到某一個因數 使得 成立,則
根據歐拉定理可知,由於 是質數,則 ,故遍歷 的所有因數。
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);
}
}
得到滿足等式 的最小n值(最小週期) 之後,由於現在的等式爲
可得只要滿足 爲 的倍數,則原式成立。
即
如果把 分解質因子得到
那麼對於每一個已知 ,可以得到
則 分解質因子爲
令
ps爲什麼要向上取整捏:假設一個數 有 的因數,那麼必須保證 至少有 個因數。但是如果 只有 個因數的話,那麼 最多隻有 個因數。 所以要就多不就少。
求出 的值,那麼 有 個可以取的值
即:遍歷所有的 ,當 ,求出 ,則可得到符合條件的 有 個,那麼所有的 之和,就是當 與 不互質時,滿足條件的 數對的數量。
但是 的範圍很廣,難道要一個個遍歷嗎。其實 中的 的值最大不超過30,那麼 中,當 時,所有的 的值與 時的 的值相同
2.當 與 不互質,即
如果滿足 ,那麼必須滿足 ,即 爲 3 的倍數
故 爲 倍數。只要 是 的倍數,那麼 一定是 的倍數。
則在此情況下滿足條件的數對數爲
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;
}