题目
点这里看题目。
分析
可以发现,符合条件的分数约分后,其分母必须为(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;
}