Live2D

Solution -「NOI 2016」「洛谷 P1587」循环之美

\(\mathcal{Description}\)

  Link.

  给定 \(n,m,k\),求 \(x\in [1,n]\cap\mathbb N,y\in [1,m]\cap \mathbb N\),且最简分数 \(\frac{x}{y}\)\(k\) 进制下是纯循环小数(包括整数)的 \((x,y)\) 数量。

  \(n,m\le10^9\)\(k\le2\times10^3\)

\(\mathcal{Solution}\)

  当你举几个十进制的纯循环小数就不难发现规律了。(

  考虑一个已有 \(x\perp y\)\(\frac{x}y\),假设它是 \(k\) 进制下的纯循环小数,且循环节长度为 \(l\)。记 \(\{x\}\) 表示 \(x\) 的小数部分值,那么有

\[\left\{\frac{xk^l}{y}\right\}=\left\{\frac{x}{y}\right\}\\\Leftrightarrow~~~~\frac{xk^l}{y}-\left\lfloor\frac{xk^l}{y}\right\rfloor=\frac{x}{y}-\left\lfloor\frac{x}{y}\right\rfloor\\\Leftrightarrow~~~~xk^l-y\left\lfloor\frac{xk^l}{y}\right\rfloor=x-y\left\lfloor\frac{x}{y}\right\rfloor\\\Leftrightarrow~~~~xk^l\equiv x\pmod y\\\Leftrightarrow~~~~k\perp y \]

所以题目就是要求

\[\sum_{i=1}^m\sum_{j=1}^n[i\perp j][i\perp k] \]


  略微推一下式子嘛:

\[\begin{aligned}\sum_{i=1}^m\sum_{j=1}^n[i\perp j][i\perp k]&=\sum_{i=1}^m[i\perp k]\sum_{j=1}^n\sum_{d\mid i,d\mid j}\mu(d)\\&=\sum_{d=1}^{\min\{n,m\}}[d\perp k]\mu(d)\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}[i\perp k]\lfloor\frac{n}{d}\rfloor\\&=\sum_{d=1}^{\min\{n,m\}}[d\perp k]\mu(d)\lfloor\frac{n}{d}\rfloor\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}[i\perp k]\end{aligned} \]

套上整除分块,分别研究两个求和,令

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

快速解决它们,就能整除分块啦。

  先考虑 \(f\),显然的事实是 \([i\perp k]=[(i\bmod k)\perp k]\),继而有

\[f(n)=\lfloor\frac{n}{k}\rfloor f(n)+f(n\bmod k) \]

注意到 \(k\) 很小,\(\mathcal O(k)\) 预处理之后就能 \(\mathcal O(1)\)\(f\) 了。

  对于 \(g\) 而言,\([i\perp k]\) 还能继续莫反——

\[\begin{aligned}g(n,k)&=\sum_{i=1}^n[i\perp k]\mu(i)\\&=\sum_{i=1}^n\mu(i)\sum_{d\mid i,d\mid k}\mu(k)\\&=\sum_{d\mid k}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu(id)\\&=\sum_{d\mid k}\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}[i\perp d]\mu(id)~~~~*\\&=\sum_{d\mid k}(\mu(d))^2g(\lfloor\frac{n}{d}\rfloor,d)\end{aligned} \]

其中,标注 \(*\) 的步骤同时利用 \(\mu\) 自身和积性函数普遍的性质进行“无用”转化,巧妙地完成了递推式。直接记忆化计算上式 就可以在可观的复杂度内求出 \(g\) 了,特别地,当 \(k=1\),需要用杜教筛求 \(\mu\) 的前缀和。

  复杂度据说是 \(\mathcal O(\sigma_0(k)n^{\frac{1}2}+n^{\frac{2}3})\)

\(\mathcal{Code}\)

/* Clearink */

#include <cmath>
#include <cstdio>
#include <unordered_map>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MAXK = 2e3, MAXS = 1e7;
int n, m, K, pn, pr[MAXS + 5];
bool vis[MAXS + 5];
int f[MAXK + 5], mu[MAXS + 5], mus[MAXS + 5];

inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline int gcd( const int a, const int b ) { return b ? gcd( b, a % b ) : a; }

inline LL calcF( const int n ) {
	return ( n / K ) * f[K] + f[n % K];
}

inline void sieve() {
	mu[1] = mus[1] = 1;
	rep ( i, 2, MAXS ) {
		if ( !vis[i] ) mu[pr[++pn] = i] = -1;
		for ( int j = 1, t; j <= pn && ( t = i * pr[j] ) <= MAXS; ++j ) {
			vis[t] = true;
			if ( !( i % pr[j] ) ) break;
			mu[t] = -mu[i];
		}
		mus[i] = mu[i] + mus[i - 1];
	}
}

inline int calcM( const int n ) {
	static std::unordered_map<int, int> mem;

	if ( n <= MAXS ) return mus[n];
	if ( mem.count( n ) ) return mem[n];

	int ret = 1;
	for ( int l = 2, r; l <= n; l = r + 1 ) {
		r = n / ( n / l );
		ret -= ( r - l + 1 ) * calcM( n / l );
	}
	return mem[n] = ret;
}

inline LL calcS( const int n, const int k ) {
	static std::unordered_map<LL, LL> mem;

	if ( !n ) return 0;
	if ( k == 1 ) return calcM( n );
	LL h = n * 2012ll + k;
	if ( mem.count( h ) ) return mem[h];

	LL ret = 0;
	rep ( i, 1, sqrt( 1. * k ) ) if ( !( k % i ) ) {
		ret += mu[i] * mu[i] * calcS( n / i, i );
		if ( i * i != k ) {
			ret += mu[k / i] * mu[k / i] * calcS( n / ( k / i ), k / i );
		}
	}
	return mem[h] = ret;
}

int main() {
	// freopen( "cyclic.in", "r", stdin );
	// freopen( "cyclic.out", "w", stdout );

	scanf( "%d %d %d", &n, &m, &K );
	
	sieve();
	rep ( i, 1, K ) f[i] = f[i - 1] + ( gcd( i, K ) == 1 );

	LL ans = 0;
	for ( int l = 1, r, t = imin( n, m ); l <= t; l = r + 1 ) {
		r = imin( n / ( n / l ), m / ( m / l ) );
		ans += ( calcS( r, K ) - calcS( l - 1, K ) )
			* ( n / l ) * calcF( m / l );
	}

	printf( "%lld\n", ans );
	return 0;
}

posted @ 2021-05-18 13:27  Rainybunny  阅读(47)  评论(0编辑  收藏  举报