• 【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;
    }
    
  • 相关阅读:
    北京大学计算机系2009应试硕士生上机考试(DF)
    我的考研2010(一)
    这张容易看懂...
    关于招商银行信用卡的若干事宜
    20 years
    C/C++中关于qsort的使用
    有道破题~~
    POJ 4010 2011
    有道难题练习赛 Sibonacci
    北京大学计算机系2009应试硕士生上机考试(AC)
  • 原文地址:https://www.cnblogs.com/crashed/p/12995181.html
Copyright © 2020-2023  润新知