# 【UOJ #221】【NOI 2016】循环之美

http://uoj.ac/problem/221

$$\frac ab$$$$k$$进制下是纯循环小数，我们先假设循环节长度为$$l$$，这样$$\frac ab\times k^l-\frac ab$$的小数部分就是0，也就是这是个整数。
$$\frac{a\left(k^l-1\right)}b$$是个整数，就是说$$b|a\left(k^l-1\right)$$。又因为$$a\bot b$$，所以$$b|\left(k^l-1\right)$$

$$k$$$$b$$不互质时，对于所有的$$l$$$$b$$$$k^l$$有共同的质因子，又因为$$k^l\bot\left(k^l-1\right)$$，所以$$b$$$$k^l$$的共同的质因子是$$k^l-1$$没有的，所以不存在$$l$$满足$$b|\left(k^l-1\right)$$
$$k$$$$b$$互质时，由欧拉定理：$$k^{\varphi(b)}\mod b=1$$，存在$$l=\varphi(b)$$

\begin{aligned} &\sum_{a=1}^n\sum_{b=1}^m[b\bot k][a\bot b]\\ =&\sum_{d=1}^n\mu(d)\sum_{a=1}^{\left\lfloor\frac nd\right\rfloor}\sum_{b=1}^{\left\lfloor\frac md\right\rfloor}[bd\bot k]\\ =&\sum_{d=1}^n\mu(d)[d\bot k]\left\lfloor\frac nd\right\rfloor\sum_{b=1}^{\left\lfloor\frac md\right\rfloor}[b\bot k] \end{aligned}

$$\sum\limits_{i=1}^n[i\bot k]$$很好预处理后$$O(1)$$计算。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const int N = 1000003;

bool notp[N];
int mu[N], sum_mu[N], prime[N], Num = 0;

void Euler_shai() {
mu[1] = sum_mu[1] = 1;
for (int i = 2; i <= 1000000; ++i) {
if (!notp[i]) prime[++Num] = i, mu[i] = -1;
for (int j = 1; j <= Num && prime[j] * i <= 1000000; ++j) {
notp[prime[j] * i] = true;
if (i % prime[j] == 0) break;
mu[prime[j] * i] = -mu[i];
}
sum_mu[i] = sum_mu[i - 1] + mu[i];
}
}

int R[N], cnt = 0, sum_mu2[N], n, m, k, S[N], pr[N], prnum = 0;

const int mo = 2333333;

struct HashTable {
int pos[mo], num[mo];

void ins(int nu, int po) {
int tmp = nu % mo;
while (num[tmp]) {++tmp; if (tmp == mo) tmp = 0;}
num[tmp] = nu;
pos[tmp] = po;
}

int query(int nu) {
int tmp = nu % mo;
while (num[tmp] != nu) {++tmp; if (tmp == mo) tmp = 0;}
return pos[tmp];
}
} HT;

int sum[N], id[N];

int gcd(int a, int b) {return b ? gcd(b, a % b) : a;}

void pre_sum() {
for (int i = 1; i <= k; ++i) {
sum[i] = sum[i - 1];
if (gcd(i, k) == 1)
++sum[i];
}
}

int cal(int num) {return sum[k] * (num / k) + sum[num % k];}

int Sum(int num) {return num <= 1000000 ? S[id[num]] : S[HT.query(num)];}

int main() {
scanf("%d%d%d", &n, &m, &k);
Euler_shai();

for (int i = 1, j = 1; i <= n && i <= m; ++i) {
j = m / (m / i);
i = n / (n / i);
if (i > j) i = j;
R[++cnt] = i;
if (i > 1000000) HT.ins(i, cnt);
}

for (int i = 1; i <= cnt; ++i) {
int num = R[i], &ret = S[i];
if (num <= 1000000) {ret = sum_mu[num]; id[num] = i; continue;}
ret = 1;
for (int j = 2, pre = 1; j <= num; pre = j, ++j) {
j = num / (num / j);
ret -= 1ll * Sum(num / j) * (j - pre);
}
}

for (int i = 1; i <= Num && prime[i] <= k; ++i)
if (k % prime[i] == 0)
pr[++prnum] = prime[i];

for (int i = 1, pi = pr[1]; i <= prnum; pi = pr[++i])
for (int j = 1; j <= cnt; ++j)
S[j] += Sum(R[j] / pi);

pre_sum();

ll ans = 0;
for (int tmp = 1, i = 1, j = 1; i <= n && i <= m; ++tmp, ++i) {
j = m / (m / i);
i = n / (n / i);
if (i > j) i = j;
ans += 1ll * (S[tmp] - S[tmp - 1]) * (n / i) * cal(m / i);
}

printf("%lld\n", ans);
return 0;
}

posted @ 2017-04-28 20:57  abclzr  阅读(463)  评论(1编辑  收藏  举报