【LGR-072】回首过去

题目

  点这里看题目。

分析

  可以发现,符合条件的分数约分后,其分母必须为\(2^m5^k\)。因此,原分数一定可以表示为:

\[\frac{XY}{2^m5^kX} \]

  其中\((10,X)=1, XY\le n, 2^m5^kX\le n\)。
  可以发现,这样枚举可以保证分母不重复,因而保证枚举出的分数不重复。
  考虑\(X\)的大小限制:

\[ 2^m5^kX \le n\Rightarrow X\le \lfloor\frac n {2^m5^k}\rfloor \]

  再考虑\(Y\)对\(X\)的影响。设阈值\(T=\lfloor\frac n{\lfloor\frac n {2^m5^k}\rfloor}\rfloor\)。当\(Y<T\)时,\(X\)受到上式的约束;当\(Y\ge T\)时,\(X\)受到条件\(XY\le n\)的约束。
  设\(p(x)=\sum_{i=1}^x[(10,i)=1]\),即\([1,x]\)中与\(2,5\)互质的数的个数,我们可以用容斥原理快速计算\(p\)的值。
  发现,如果用\(f(y)\)表示当\(Y=y\)时,\(X\)的取值的个数,那么它就是一个分段函数:

\[f(y)= \begin{cases} p(\lfloor\frac n {2^m5^k}\rfloor) & y<T\\ p(\lfloor\frac n y\rfloor) & y \ge T \end{cases} \]

  当\(m,k\)确定时,答案即为\(\sum_{i=1}^n f(i)\)。当\(y<T\)时,贡献可以直接暴力计算;当\(y>T\)时,我们可以预处理前缀和。
  \(m\)和\(k\)都可以枚举,因此计算答案的时间是\(O(n+\log_2n\log_5n)\)。
  恭喜,有 80pts 了
  考虑优化,由于\(T\)的取值数量为\(O(\sqrt n)\),因此我们可以直接数论分块,留下需要的点求出前缀和。时间复杂度\(O(\sqrt n + \log_2n\log_5n)\)。

代码

#include <cmath>
#include <cstdio>

typedef long long LL;

#define int LL

const int MAXN = 2e6 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

LL su[MAXN], seq[MAXN], p2[MAXN], p5[MAXN];
int id1[MAXN], id2[MAXN];
LL N, rt, ID;

int& getID( const LL v )
{
	if( v <= rt ) return id1[v];
	return id2[N / v];
}

LL query( LL up ) { return up - up / 2 - up / 5 + up / 10; }

signed main()
{
	read( N ), rt = sqrt( N );
	for( LL l = 1, r, v ; l <= N ; l = r + 1 )
	{
		r = N / ( v = N / l );
		seq[++ ID] = v, getID( v ) = ID;
	}
	for( int i = ID ; i ; i -- ) su[i] = su[i + 1] + ( seq[i] - seq[i + 1] ) * query( N / seq[i] );
	LL ans = 0;
	int siz2, siz5;
	p2[0] = 1, p5[0] = 1;
	for( siz2 = 0 ; p2[siz2] <= N ; ) siz2 ++, p2[siz2] = p2[siz2 - 1] << 1;
	for( siz5 = 0 ; p5[siz5] <= N ; ) siz5 ++, p5[siz5] = p5[siz5 - 1] * 5;
	for( int i = 0 ; i < siz2 ; i ++ )
		for( int j = 0 ; j < siz5 && p2[i] * p5[j] <= N ; j ++ )
			if( p2[i] * p5[j] <= N )
			{
				LL tmp = N / ( N / p2[i] / p5[j] );
				ans += su[getID( N )] - su[getID( tmp )] + query( N / tmp ) + ( tmp - 1 ) * query( N / p2[i] / p5[j] );
			}
	write( ans ), putchar( '\n' );
	return 0;
}
上一篇:寒武纪智能系统参数


下一篇:leetcode 264. 丑数 II