P1587 [NOI2016]循环之美

P1587 [NOI2016]循环之美

显然只用统计最简分数的情况,否则会算重。

原题等价于:(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)


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

不会了。。。。

(kle2000???)

[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] ]

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

[sum_{i=1}^{n}[gcd(i,k)==1]=lfloordfrac{n}{k} floor imesvarphi(k)+s(n\%k) ]

其中 (s(n)=sumlimits_{i=1}^{n}[gcd(i,k)==1])(Huge kle 2000!!!)(O(klog 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) ot=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)^2sum_{i=1}^{frac{n}{d}}[gcd(i,d)==1]mu(i) ]

(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

路漫漫其修远兮,吾将上下而求索
原文地址:https://www.cnblogs.com/zzctommy/p/13771623.html