Loading

P1587 [NOI2016]循环之美

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\)


\[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)==1] [\gcd(j,k)==1] \]

不会了。。。。

\(k\le2000???\)

\[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)==1] [\gcd(j,k)==1]\\ =\sum_{j=1}^{m}[\gcd(j,k)==1]\sum_{i=1}^{n}[\gcd(i,j)==1]\\ =\sum_{j=1}^{m}[\gcd(j,k)==1]\sum_{i=1}^{n}\sum_{d|\gcd(i,j)}\mu(d)\\ =\sum_{d=1}^{n}\mu(d)\sum_{j=1}^{\frac{m}{d}}[\gcd(jd,k)==1]\sum_{i=1}^{\frac{n}{d}}1\\ =\sum_{d=1}^{n}\mu(d)\dfrac{n}{d}\sum_{j=1}^{\frac{m}{d}}[\gcd(jd,k)==1]\\ =\sum_{d=1}^{n}[\gcd(d,k)==1]\mu(d)\dfrac{n}{d}\sum_{j=1}^{\frac{m}{d}}[\gcd(j,k)==1] \]

先看后半部分 \(\sum\limits_{i=1}^{n}[\gcd(i,k)==1]\) ,非常一眼:

\[\sum_{i=1}^{n}[\gcd(i,k)==1]=\lfloor\dfrac{n}{k}\rfloor\times\varphi(k)+s(n\%k) \]

其中 \(s(n)=\sum\limits_{i=1}^{n}[\gcd(i,k)==1]\)\(\Huge k\le 2000!!!\)\(O(k\log k)\) 记个前缀和就可以预处理出来。

这部分就可以 \(O(1)\) 啦!

看另一部分:

\[\sum_{i=1}^{n}[\gcd(i,k)==1]\mu(i)\\ =\sum_{i=1}^{n}\mu(i)\sum_{d|\gcd(i,k)}\mu(d)\\ =\sum_{d|k}\mu(d)\sum_{i=1}^{\frac{n}{d}}\mu(id) \]

\(\gcd(i,d)\not=1\) 时, \(id\) 必有平方因子,即 \(\mu(id)=0\)

所以只用计算 \(\gcd(i,d)=1\) 时的情况。

由于 \(\mu\) 是积性函数,在 \(i,d\) 互质的情况下 \(\mu(id)=\mu(i)\mu(d)\)

带回去:

\[=\sum_{d|k}\mu(d)\sum_{i=1}^{\frac{n}{d}}[\gcd(i,d)==1]\mu(id)\\ =\sum_{d|k}\mu(d)\sum_{i=1}^{\frac{n}{d}}[\gcd(i,d)==1]\mu(i)\mu(d)\\ =\sum_{d|k}\mu(d)^2\sum_{i=1}^{\frac{n}{d}}[\gcd(i,d)==1]\mu(i) \]

\(\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

posted @ 2020-10-05 21:12  zzctommy  阅读(158)  评论(0编辑  收藏  举报