首先发出题目链接:
链接: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;
}