poj 3696:
题意:
输入一个数L(1 <= L <= 2,000,000,000)。。。
求能整除L且所有位上的数字都是8的数的最小位是几位。
解析:
高中数学课上安哥讲这个的时候真心觉得屌,一个有x位且每位数字都有相同的数字u的数,可以这样表示:
(10 ^ x - 1) / 9 × u 。
这题,设长度为x的由8组成的数可以被L整除,则:
(10 ^ x - 1 ) / 9 * 8 = L * p .
即: 8 * (10 ^ x - 1 ) = L * p * 9 .
设: d = gcd( 8, L ) .
则: 8 / d * (10 ^ x - 1 ) = L / d * 9 * p .
设: 8 / d 为 a, L / d * 9为b . 易知 a, b 互素 .
则: a * (10 ^ x - 1 ) = b * p .
由于 a, b 互素: 10 ^ x = 1 (mod b)
ps. 设 a * c = b * p, a,b互素,可知 a * c = 0 (mod b),又由于a,b互素,所以:c = 0 (mod b).
所以方程转换为求同余方程10 ^ x = 1 (mod b)的最小x解。
若gcd(10, b) != 1,则原方程无解;
若有解,根据欧拉公式可以知道 10 ^ phi(b) = 1 mod (b),枚举phi(b)的因子,找到最小的满足欧拉公式的数即可。
注意点是快速幂里面还要再套快速乘,因为超LL,只能转成加法做。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
using namespace std;
const int maxn = (1 << 16);
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = 4 * atan(1.0);
const double ee = exp(1.0);
LL euler_phi(LL n)
{
LL m = sqrt(n + 0.5);
LL res = n;
for (LL i = 2; i <= m; i++)
{
if (n % i == 0)
{
res = res / i * (i - 1);
while (n % i == 0)
n /= i;
}
}
if (1 < n)
res = res / n * (n - 1);
return res;
}
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
LL mult_mod(LL a, LL b, LL mod)
{
LL res = 0;
while (b)
{
if (b & 1)
res = (res + a) % mod;
a = 2 * a % mod;
b >>= 1;
}
return res;
}
LL pow_mod(LL a, LL n, LL mod)
{
if (n == 0)
return 1;
LL x = pow_mod(a, n >> 1, mod);
LL res = mult_mod(x, x, mod);
if (n % 2)
res = mult_mod(res, a, mod);
return res;
}
int cnt;
LL factor[maxn];
void Divid(LL t)
{
cnt = 0;
memset(factor, 0, sizeof(factor));
for (LL i = 1; i * i <= t; i++)
{
if (t % i == 0)
{
factor[cnt++] = i;
factor[cnt++] = t / i;
}
}
sort(factor, factor + cnt);
}
LL solve(LL m)
{
LL t = euler_phi(m);
Divid(t);
for (int i = 0; i < cnt; i++)
if (pow_mod(10, factor[i], m) == 1)
return factor[i];
return 0;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ca = 1;
LL m;
while (scanf("%lld", &m) && m)
{
printf("Case %d: ", ca++);
m = m / gcd(m, 8) * 9;
if (gcd(10, m) != 1)
{
printf("0\n");
}
else
{
printf("%lld\n", solve(m));
}
}
return 0;
}
poj 3358:
题意:
给pq,代表一个小数p/q,求在二进制下这个小数的最小循环前缀和最小循环起始位。
解析:
小数二进制的转换方法就是不断*2,mod 分母。
比如:1/10 2/10 4/10 8/10 16/10 32/10...
模后:1/10 2/10 4/10 8/10 6/10 2/10...
这时出现了重复,并且这个重复就是最小循环。
设从x位置到y位置循环节的分子表示为:2^y = 2^x (mod p)
稍微变身一下:2^y - 2^x = 0 (mod p) ,
2^x(2^(y - x ) - 1) = 0 (mod p),
所以: p | 2^x(2^(y - x ) - 1),把p一直除到不能被整除就是最小的循环部分起始位置x,
然后上式变成了:2^(y-x) - 1 = 0 (mod p').
即: 2^(y - x) = 1 (mod p').
又到达了欧拉定理的某种设定之中,根据欧拉定理,t = y - x = elur_phi(p'),枚举同上从小到大枚举 t 的约数,满足此式子就得解。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
using namespace std;
const int maxn = (1 << 16);
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = 4 * atan(1.0);
const double ee = exp(1.0);
LL euler_phi(LL n)
{
LL m = sqrt(n + 0.5);
LL res = n;
for (LL i = 2; i <= m; i++)
{
if (n % i == 0)
{
res = res / i * (i - 1);
while (n % i == 0)
n /= i;
}
}
if (1 < n)
res = res / n * (n - 1);
return res;
}
LL gcd(LL a, LL b)
{
return b ? gcd(b, a % b) : a;
}
LL mult_mod(LL a, LL b, LL mod)
{
LL res = 0;
while (b)
{
if (b & 1)
res = (res + a) % mod;
a = 2 * a % mod;
b >>= 1;
}
return res;
}
LL pow_mod(LL a, LL n, LL mod)
{
if (n == 0)
return 1;
LL x = pow_mod(a, n >> 1, mod);
LL res = mult_mod(x, x, mod);
if (n % 2)
res = mult_mod(res, a, mod);
return res;
}
int cnt;
LL factor[maxn];
void Divid(LL t)
{
cnt = 0;
memset(factor, 0, sizeof(factor));
for (LL i = 1; i * i <= t; i++)
{
if (t % i == 0)
{
factor[cnt++] = i;
factor[cnt++] = t / i;
}
}
sort(factor, factor + cnt);
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ca = 1;
LL m, n;
LL ans1, ans2;
while (scanf("%lld/%lld", &n, &m) != EOF)
{
LL d = gcd(n, m);
n /= d;
m /= d;
LL t = 1;
while (m % 2 == 0)
{
t++;
m /= 2;
}
ans1 = t;
LL phi = euler_phi(m);
if (phi == 1)
{
ans2 = 1;
}
else
{
Divid(phi);
for (int i = 0; i < cnt; i++)
{
if (pow_mod(2, factor[i], m) == 1)
{
ans2 = factor[i];
break;
}
}
}
printf("Case #%d: %lld,%lld\n", ca++, ans1, ans2);
}
return 0;
}