【LGR-072】回首过去

题目

点这里看题目。

分析

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

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

其中((10,X)=1, XYle n, 2^m5^kXle n)

可以发现,这样枚举可以保证分母不重复,因而保证枚举出的分数不重复。

考虑(X)的大小限制:

[ 2^m5^kX le nRightarrow Xle lfloorfrac n {2^m5^k} floor ]

再考虑(Y)(X)的影响。设阈值(T=lfloorfrac n{lfloorfrac n {2^m5^k} floor} floor)。当(Y<T)时,(X)受到上式的约束;当(Yge T)时,(X)受到条件(XYle n)的约束。

(p(x)=sum_{i=1}^x[(10,i)=1]),即([1,x])中与(2,5)互质的数的个数,我们可以用容斥原理快速计算(p)的值。

发现,如果用(f(y))表示当(Y=y)时,(X)的取值的个数,那么它就是一个分段函数:

[f(y)= egin{cases} p(lfloorfrac n {2^m5^k} floor) & y<T\ p(lfloorfrac n y floor) & y ge T end{cases} ]

(m,k)确定时,答案即为(sum_{i=1}^n f(i))。当(y<T)时,贡献可以直接暴力计算;当(y>T)时,我们可以预处理前缀和。

(m)(k)都可以枚举,因此计算答案的时间是(O(n+log_2nlog_5n))

恭喜,有 80pts 了

考虑优化,由于(T)的取值数量为(O(sqrt n)),因此我们可以直接数论分块,留下需要的点求出前缀和。时间复杂度(O(sqrt n + log_2nlog_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( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/12995181.html