P1587 [NOI2016]循环之美
显然只用统计最简分数的情况,否则会算重。
原题等价于:\(1\le i \le n,1\le j \le m\) 有几个最简分数 \(\dfrac{i}{j}\) 在 \(k\) 进制下是纯循环小数
\(\color{black}{\texttt {c}}\color{red}{\texttt {yn2006}}\) 说一个最简分数 \(\dfrac{x}{y}\) 在 \(k\) 进制下是纯循环小数当且仅当 \(\gcd(y,k)=1\) 。
其实一开始也是类比十进制纯循环小数去推,但是没成功/kk
后来 \(\color{black}{\texttt {c}}\color{red}{\texttt {yn2006}}\) 随手一推就推出来还把我D了一顿。。。
证明:
若一个分数 \(\dfrac{x}{y}\) 在 \(k\) 进制下是纯循环小数,那么必然存在正整数 \(l\) 满足 \(k^l\times\dfrac{x}{y}-\lfloor k^l\times\dfrac{x}{y} \rfloor = \dfrac{x}{y}\)
两边同时乘上 \(y\) 得 \(x\times k^l-y\times \lfloor k^l\times\dfrac{x}{y} \rfloor=x\),由于两边相等,那么同时 \(\mod y\) 也相等。
化简得\(x\times k^l\equiv x \pmod {y}\) ,因为 \(\gcd(x,y)=1\) ,所以可以消去 \(x\) , 即 \(k^l\equiv 1\pmod y\) 。
显然,若 \(\gcd(k^l,y)=1\) 那么 \(\gcd(k,y)=1\) 。
不会了。。。。
\(k\le2000???\)
先看后半部分 \(\sum\limits_{i=1}^{n}[\gcd(i,k)==1]\) ,非常一眼:
其中 \(s(n)=\sum\limits_{i=1}^{n}[\gcd(i,k)==1]\) ,\(\Huge k\le 2000!!!\), \(O(k\log k)\) 记个前缀和就可以预处理出来。
这部分就可以 \(O(1)\) 啦!
看另一部分:
当 \(\gcd(i,d)\not=1\) 时, \(id\) 必有平方因子,即 \(\mu(id)=0\)
所以只用计算 \(\gcd(i,d)=1\) 时的情况。
由于 \(\mu\) 是积性函数,在 \(i,d\) 互质的情况下 \(\mu(id)=\mu(i)\mu(d)\)
带回去:
\(\color{black}{\texttt {c}}\color{red}{\texttt {yn2006}}\) 告诉我可以递归。
恍然大悟.jpg
令 \(f(n,k)=\sum\limits_{i=1}^{n}[\gcd(i,k)==1]\mu(i)\)
那么有 \(f(n,k)=\sum\limits_{d|k}\mu(d)^2f(\dfrac{n}{d},d)\) ,可以递归了
那个 \(\mu(d)^2\) 怎么办啊?
\(\color{black}{\texttt {c}}\color{red}{\texttt {yn2006}}\) 怒斥:\(\Huge k\le2000!!!\)
于是暴力 \(\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\n", ans);
return 0;
}
整除分块写挂身败名裂*114514
前缀和写挂身败名裂*1919810