显然只用统计最简分数的情况,否则会算重。
原题等价于:(1le i le n,1le j le m) 有几个最简分数 (dfrac{i}{j}) 在 (k) 进制下是纯循环小数
(color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 说一个最简分数 (dfrac{x}{y}) 在 (k) 进制下是纯循环小数当且仅当 (gcd(y,k)=1) 。
其实一开始也是类比十进制纯循环小数去推,但是没成功/kk
后来 (color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 随手一推就推出来还把我D了一顿。。。
证明:
若一个分数 (dfrac{x}{y}) 在 (k) 进制下是纯循环小数,那么必然存在正整数 (l) 满足 (k^l imesdfrac{x}{y}-lfloor k^l imesdfrac{x}{y} floor = dfrac{x}{y})
两边同时乘上 (y) 得 (x imes k^l-y imes lfloor k^l imesdfrac{x}{y} floor=x),由于两边相等,那么同时 (mod y) 也相等。
化简得(x imes k^lequiv x pmod {y}) ,因为 (gcd(x,y)=1) ,所以可以消去 (x) , 即 (k^lequiv 1pmod y) 。
显然,若 (gcd(k^l,y)=1) 那么 (gcd(k,y)=1) 。
不会了。。。。
(kle2000???)
先看后半部分 (sumlimits_{i=1}^{n}[gcd(i,k)==1]) ,非常一眼:
其中 (s(n)=sumlimits_{i=1}^{n}[gcd(i,k)==1]) ,(Huge kle 2000!!!), (O(klog k)) 记个前缀和就可以预处理出来。
这部分就可以 (O(1)) 啦!
看另一部分:
当 (gcd(i,d) ot=1) 时, (id) 必有平方因子,即 (mu(id)=0)
所以只用计算 (gcd(i,d)=1) 时的情况。
由于 (mu) 是积性函数,在 (i,d) 互质的情况下 (mu(id)=mu(i)mu(d))
带回去:
(color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 告诉我可以递归。
恍然大悟.jpg
令 (f(n,k)=sumlimits_{i=1}^{n}[gcd(i,k)==1]mu(i))
那么有 (f(n,k)=sumlimits_{d|k}mu(d)^2f(dfrac{n}{d},d)) ,可以递归了
那个 (mu(d)^2) 怎么办啊?
(color{black}{ exttt {c}}color{red}{ exttt {yn2006}}) 怒斥:(Huge kle2000!!!)
于是暴力 (sqrt{k}) 跑因数的复杂度变对了qwq
边界?
(n=0) 的时候显然是 (0)
(k=1) 的时候是 (sum_{i=1}^{n}mu(i)) ,杜教筛。
然后递归就好了!
一路递归回去找式子.jpg
做完了诶!整除分块一下式子就没了!
另外,递归的时候记得加记忆化,这样复杂度就是 (O(operatorname{d}(n)sqrt{n}+n^{frac{2}{3}}))
其中 (operatorname{d}(i)) 表示 (i) 的约数个数
给两个大样例以供调试
Input#1
1000000000 99999999 1926
Output#1
30114906314949722
Input#2
904 791040170 780
Output#2
168263165839
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int N = 1000005;
const int C = 1000000;
const int K = 2005;
int n, m, k;
int mu[N], sm[N], pri[N / 9], cnt, s[K];
bool vis[N];
LL ans;
unordered_map <int, int> _mu, dp[K];
int gcd(int x, int y) {return !y ? x : gcd(y, x % y);}
void init() {
mu[1] = 1;
for (int i = 2; i <= C; ++ i) {
if (!vis[i]) pri[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * pri[j] <= C; ++ j) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = - mu[i];
}
}
for (int i = 1; i <= C; ++ i) sm[i] = mu[i] + sm[i - 1];
for (int i = 1; i <= k; ++ i) s[i] = s[i - 1] + (gcd(i, k) == 1);
}
int smu(int x) {
if (x <= C) return sm[x];
if (_mu[x]) return _mu[x];
int res = 1;
for (int l = 2, r; l <= x; l = r + 1)
r = x / (x / l), res -= (r - l + 1) * smu(x / l);
return _mu[x]=res;
}
int h(int x) {return s[k] * (x / k) + s[x % k];}//for i 1 x :[gcd(i,k)=1]
int g(int n, int k) {
if (!n) return 0;
if (k == 1) return dp[k][n] = smu(n);
if (dp[k][n]) return dp[k][n];
int res = 0;
for (int i = 1; i * i <= k; ++ i) {
if (k % i == 0) {
if (mu[i]) res += mu[i] * mu[i] * g(n / i, i);
if (mu[k / i] && i * i != k) res += mu[k / i] * mu[k / i] * g(n / (k / i), k / i);
}
}
return dp[k][n] = res;
}
signed main(){
n = read(), m = read(), k = read(), init();
for (int l = 1, r, mx = min(n, m); l <= mx; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += 1ll * (n / l) * h(m / l) * (g(r, k) - g(l - 1, k));
}
printf("%lld
", ans);
return 0;
}
整除分块写挂身败名裂*114514
前缀和写挂身败名裂*1919810