[LOCAL] gcd 求和

题目

题目大意:

对于给定的 \(n,m\) ,求:

\[\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j) \]

数据范围:

\(\text{task_id}\) \(n,m\le\) \(T\le\) 特殊性质
\(1\) \(10\) \(10^3\)
\(2\) \(10^3\) \(10\)
\(3\) \(10^6\) \(10^4\)
\(4\) \(10^6\) \(10\) \(n=m\)
\(5\) \(10^6\) \(10^4\) \(n=m\)
\(6\) \(10^6\) \(10^5\) \(n=m\)
\(7\) \(10^7\) \(10^6\) \(n=m\)
\(8\) \(10^6\) \(10\)
\(9\) \(10^6\) \(10^4\)

分析

注意数据范围:当 \(n=m\) 时,数据范围较大,因此我们需要分类计算。

  • \(n\not=m\)

    此时可以直接反演,得到结果为:

    \[\sum_{T=1}^{\min\{n,m\}}\lfloor\frac n T\rfloor\lfloor\frac m T\rfloor\varphi(T) \]

  • \(n=m\)

    此时如果套用反演的结果我们可以愉快地得到:

    \[\sum_{T=1}^n\lfloor\frac n T\rfloor^2\varphi(T) \]

    ......当然是没有办法做的。

    注意到 \(n=m\) 其实是很强的条件,它直接给我们减少了一个变量,因此可以设 \(f(n)=\sum_{i=1}^n\sum_{j=1}^n\gcd(i,j)\)

    既然直接反演无解,我们不妨用点技巧:对 \(f\) 进行差分。

    \[\begin{aligned} \Delta f(n) &=f(n+1)-f(n)\\ &=2\sum_{i=1}^{n+1}\gcd(i,n+1)-(n+1)\\ &=2\sum_{d|n+1}d\sum_{i=1}^{\frac{n+1}{d}}[(i,d)=1]-(n+1)\\ &=2\sum_{d|n+1}d\varphi(\frac{n+1}{d})-(n+1) \end{aligned} \]

    注意到 \(id*\varphi\) 是积性函数,因此我们可以线筛筛出 \(g(n)=\sum_{d|n}d\varphi(\frac n d)\) 。因而我们可以 \(O(n)\) 求出 \(f\) ,最后 \(O(1)\) 回答单个询问。

小结:

  1. 差分是处理函数(尤其是前缀求和、后缀求和)的有效方式
  2. 思维不应局限,如果反演行不通那么久有必要试试其它方法,例如差分。

代码

#include <cstdio>

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

typedef long long LL;

const int MAXN = 1e7 + 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;
}

namespace Normal
{
	LL val[MAXN];
	int pure[MAXN];
	int prime[MAXN], pn;
	bool isPrime[MAXN];

	int N, M;

	void EulerSieve( const int siz )
	{
		val[1] = 1;
		for( int i = 2 ; i <= siz ; i ++ )
		{
			if( ! isPrime[i] ) 
				val[prime[++ pn] = i] = i - 1, pure[i] = 1;
			for( int j = 1 ; j <= pn && 1ll * i * prime[j] <= siz ; j ++ )
			{
				isPrime[i * prime[j]] = true;
				if( ! ( i % prime[j] ) )
				{
					if( ( pure[i * prime[j]] = pure[i] ) == 1 )
						val[i * prime[j]] = i * prime[j] - i;
					else
						val[i * prime[j]] = val[pure[i]] * val[i * prime[j] / pure[i]];
					break;
				}
				val[i * prime[j]] = val[pure[i * prime[j]] = i] * val[prime[j]];
			}
		}
		for( int i = 1 ; i <= siz ; i ++ ) val[i] += val[i - 1];
	}
	
	void Solve()
	{
		int T; EulerSieve( 1e6 );
		for( read( T ) ; T -- ; )
		{
			LL ans = 0;
			read( N ), read( M );
			for( int l = 1, r ; l <= N && l <= M ; l = r + 1 )
			{
				r = MIN( N / ( N / l ), M / ( M / l ) );
				ans += 1ll * ( N / l ) * ( M / l ) * ( val[r] - val[l - 1] );
			}
			write( ans ), putchar( '\n' );
		}
	}
}

namespace Special
{
	LL val[MAXN], f[MAXN]; int pure[MAXN];
	int prime[MAXN], pn;
	bool isPrime[MAXN];
	
	int N, M;
	
	void EulerSieve( const int siz )
	{
		val[1] = 1;
		for( int i = 2 ; i <= siz ; i ++ )
		{
			if( ! isPrime[i] ) val[prime[++ pn] = i] = 2 * i - 1, pure[i] = 1;
			for( int j = 1 ; j <= pn && 1ll * i * prime[j] <= siz ; j ++ )
			{
				isPrime[i * prime[j]] = true;
				if( ! ( i % prime[j] ) ) 
				{
					if( ( pure[i * prime[j]] = pure[i] ) == 1 )
						val[i * prime[j]] = val[i] * prime[j] + 1ll * ( prime[j] - 1 ) * i;
					else val[i * prime[j]] = val[pure[i]] * val[i * prime[j] / pure[i]];
					break; 
				}
				val[i * prime[j]] = val[pure[i * prime[j]] = i] * val[prime[j]];
			}
		}
		for( int i = 1 ; i <= siz ; i ++ ) f[i] = f[i - 1] + 2 * val[i] - i;
	}
	
	void Solve()
	{
		int T; EulerSieve( 1e7 );
		for( read( T ) ; T -- ; )
		{
			read( N ), read( M );
			write( f[N] ), putchar( '\n' );
		}
	}
}

int main()
{
	int task_id;
	read( task_id );
	if( task_id == 6 || task_id == 7 ) Special :: Solve();
	else Normal :: Solve();
	return 0;
}
posted @ 2021-02-01 20:23  crashed  阅读(160)  评论(0编辑  收藏  举报