P1587 [NOI2016] 循环之美 解题报告

P1587 [NOI2016] 循环之美 解题报告

link

题目大意

求所有满足 1 ≤ x ≤ n , 1 ≤ y ≤ m 1\le x\le n,1\le y\le m 1xn,1ym 且使得 x y \dfrac x y yx k k k 进制下是纯循环小数且值不相等的 ( x , y ) (x,y) (x,y) 的对数。

1 ≤ x , y ≤ 1 0 9 , 2 ≤ k ≤ 2000. 1\le x,y\le 10^9,2\le k\le 2000. 1x,y109,2k2000.

解题报告

容易转化,

x y \dfrac x y yx k k k 进制下是纯循环小数 ⇔ \Leftrightarrow y ⊥ k y\perp k yk

那么我们要统计的即
∑ i = 1 n ∑ j = 1 m [ i ⊥ j ] [ j ⊥ k ] = ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j μ ( d ) [ j ⊥ k ] = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) [ d ⊥ k ] ⌊ n d ⌋ ∑ j = 1 m / d [ j ⊥ k ] \sum_{i=1}^n\sum_{j=1}^m[i\perp j][j\perp k] \\ =\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}\mu(d)[j\perp k] \\ =\sum_{d=1}^{\min(n,m)}\mu(d)[d\perp k]\lfloor \dfrac n d\rfloor \sum_{j=1}^{m/d}[j\perp k] i=1nj=1m[ij][jk]=i=1nj=1mdi,djμ(d)[jk]=d=1min(n,m)μ(d)[dk]dnj=1m/d[jk]
我们发现我们搞出了一个整除分块的形式。


g ( n , k ) = ∑ i = 1 n μ ( i ) [ i ⊥ k ] f ( n , k ) = ∑ i = 1 n [ i ⊥ k ] g(n,k)=\sum_{i=1}^n\mu(i)[i\perp k] \\ f(n,k)=\sum_{i=1}^n[i\perp k] g(n,k)=i=1nμ(i)[ik]f(n,k)=i=1n[ik]

只要我们可以快速求在所有特殊点 x x x 处的 g ( x , k ) , f ( x , k ) g(x,k),f(x,k) g(x,k),f(x,k),这题就大功告成了。

我们考虑递推地求 f ( ⋅ , k ) , g ( ⋅ , k ) f(\cdot ,k),g(\cdot,k) f(,k),g(,k)

假设 k = p 1 p 2 ⋯ p t k=p_1p_2\cdots p_t k=p1p2pt。这是因为 k k k 有没有平方因子对 互质 这一限制没有影响。

我们考虑 k k k 如何从 k p \dfrac kp pk 中递推得到。( p p p k k k 的质因子)
f ( n , k ) = ∑ i = 1 n [ i ⊥ k ] = ∑ i = 1 n [ i ⊥ k p ] − ∑ i = 1 n [ i ⊥ k p ] [ p ∣ i ] = f ( n , k p ) − ∑ i = 1 n / p [ i ⊥ k p ] [ p ⊥ k p ] = f ( n , k p ) − f ( n / p , k p ) f(n,k)=\sum_{i=1}^n[i\perp k] \\ =\sum_{i=1}^n[i\perp \dfrac kp]-\sum_{i=1}^n[i\perp \dfrac kp][p|i] \\ = f(n,\dfrac kp) - \sum_{i=1}^{n/p}[i\perp \dfrac kp][p\perp \dfrac kp] \\ =f(n,\dfrac kp)-f(n/p,\dfrac kp) f(n,k)=i=1n[ik]=i=1n[ipk]i=1n[ipk][pi]=f(n,pk)i=1n/p[ipk][ppk]=f(n,pk)f(n/p,pk)
类似地,
g ( n , k ) = ∑ i = 1 n μ ( i ) [ i ⊥ k ] = ∑ i = 1 n μ ( i ) [ i ⊥ k p ] − ∑ i = 1 n μ ( i ) [ i ⊥ k p ] [ p ∣ i ] = g ( n , k p ) − ∑ i = 1 n / p μ ( i p ) [ p i ⊥ k p ] = g ( n , k p ) − ∑ i = 1 n / p μ ( i ) μ ( p ) [ i ⊥ p ] [ i ⊥ k p ] = g ( n , k p ) − μ ( p ) ∑ i = 1 n / p μ ( i ) [ i ⊥ k ] = g ( n , k p ) + g ( n / p , k ) g(n,k)=\sum_{i=1}^n\mu(i)[i\perp k] \\ = \sum_{i=1}^n\mu(i)[i\perp \dfrac kp]-\sum_{i=1}^n\mu(i)[i\perp \dfrac kp][p | i] \\ = g(n,\dfrac kp)-\sum_{i=1}^{n/p}\mu(ip)[pi\perp \dfrac kp] \\ =g(n,\dfrac kp)-\sum_{i=1}^{n/p}\mu(i)\mu(p)[i\perp p][i\perp \dfrac kp] \\ =g(n,\dfrac kp)-\mu(p)\sum_{i=1}^{n/p}\mu(i)[i\perp k] \\ =g(n, \dfrac kp)+g(n/p,k) g(n,k)=i=1nμ(i)[ik]=i=1nμ(i)[ipk]i=1nμ(i)[ipk][pi]=g(n,pk)i=1n/pμ(ip)[pipk]=g(n,pk)i=1n/pμ(i)μ(p)[ip][ipk]=g(n,pk)μ(p)i=1n/pμ(i)[ik]=g(n,pk)+g(n/p,k)
而递归边界为
f ( 0 , k ) = 0 , f ( n , 0 ) = n g ( 0 , k ) = 0 , g ( n , 0 ) = ∑ i = 1 n μ ( i ) f(0,k)=0,f(n,0)=n\\ g(0,k)=0,g(n,0)=\sum_{i=1}^n\mu(i) f(0,k)=0,f(n,0)=ng(0,k)=0,g(n,0)=i=1nμ(i)
其中 g ( n , 0 ) g(n,0) g(n,0) 可以杜教筛算出所有特殊点处的值.

那么我们只要花费 O ( n ⋅ d p ( k ) ) \mathcal O(\sqrt n \cdot d_p(k)) O(n dp(k)) ( d p ( k ) d_p (k) dp(k) 表示 k k k 的质因子个数)的时间即可得到所有特殊点处的值.

总时间复杂度为 O ( n ⋅ d p ( k ) + n 2 / 3 ) \mathcal O(\sqrt n \cdot d_p(k)+n^{2/3}) O(n dp(k)+n2/3).

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
ll read() {
	ll x = 0, f = 1; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
	return x * f;
}
#define mp make_pair
const int MAXN = 1e9+5, XN = 1e6+5;
const int MAXK = 2e3+5;
int n, m, K, ip[XN], pr[XN], tot, pK[100], totK;
ll mu[XN], smu0[XN];
void init(int n) {
	ip[1] = 1; mu[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!ip[i]) {pr[++tot] = i; mu[i] = -1;}
		for(int j = 1; j <= tot && 1ll * i * pr[j] <= n; j++) {
			ip[i * pr[j]] = 1;
			if(i % pr[j]) {
				mu[i * pr[j]] = -mu[i];
			} else {
				mu[i * pr[j]] = 0;
				break;
			}
		}
	}
	for(int i = 1; i <= n; i++) smu0[i] = smu0[i-1] + mu[i];
}
ll calcsmu(ll n) {
	if(n < XN) return smu0[n];
	static map<ll, ll> table;
	map<ll, ll>::iterator it;
	if((it = table.find(n)) != table.end()) return it->second;
	ll ret = 1;
	for(ll i = 2, j; i <= n; i = j+1) {
		j = n / (n / i);
		ret -= (j - i + 1) * calcsmu(n / i);
	}
	table[n] = ret;
	return ret;
}
ll calcf(ll n, int k) {
	if(n == 0) return 0;
	if(k == 0) return n;
	static map<pair<ll, int>, ll> table;
	map<pair<ll, int>, ll>::iterator it;
	if((it = table.find(mp(n, k))) != table.end()) return it->second;
	ll ret = calcf(n, k-1) - calcf(n / pK[k], k-1);
	table[mp(n, k)] = ret;
	return ret;
}
ll calcg(ll n, int k) {
	if(n == 0) return 0;
	if(k == 0) return calcsmu(n);
	static map<pair<ll, int>, ll> table;
	map<pair<ll, int>, ll>::iterator it;
	if((it = table.find(mp(n, k))) != table.end()) return it->second;
	ll ret = calcg(n, k-1) + calcg(n / pK[k], k);
	table[mp(n, k)] = ret;
	return ret;
}
int main() {
	init(XN-1);
	n = read(), m = read(), K = read();
	int t = K;
	for(int i = 2; i * i <= t; i++)
		if(t % i == 0) {
			pK[++totK] = i;
			while(t % i == 0) t /= i;
		}
	if(t > 1) pK[++totK] = t;
	ll ret = 0;
	for(ll i = 1, j; i <= n && i <= m; i = j+1) {
		j = min(n / (n / i), m / (m / i));
		ret += (calcg(j, totK) - calcg(i-1, totK)) * (n / i) * calcf(m / i, totK);
	}
	printf("%lld\n", ret);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值