Lucky酷爱gcd

这是本笱蒻写的第一个数论题的题解,原题链接LUCKY酷爱GCD ,感觉自己好菜啊 \(qwq\)

《关于开赛就暴力写了一发预测难度最大的题然后就超时一事》,比赛时以为是签到题上来就莽了一发,\(TLE\) ,实际上是赛后补了几天才会的题

思路

题意很简洁(毕竟公式都给了),求 \(\sum_{i = 1}^n \dfrac i {\gcd(i, n)}\) 的值

多组输入,数据范围是 \(t \leqslant 10^6,1 \leqslant n \leqslant 4000000\) .

推公式其实不算太难(\(maybe\)​),反正好多关键的部分是从网上查到的,具体过程如下

\(\sum_{i = 1}^n \dfrac i {\gcd(i, n)} = \sum_{d \mid n}\sum_{i = 1}^n \dfrac {i}{d} [\gcd(i, n) == d] (*)\)

\(id= i^{\prime}\) (其中 \(i^{\prime}\)\((*)\) 式中的 \(i\)​ ,则有

\[\begin{aligned} (*) & = \sum_{d \mid n}\sum_{i = 1}^{\dfrac n d} \dfrac {i * d}{d} [\gcd(i * d, n) == d] \\ & = \sum_{d \mid n}\sum_{i = 1}^{\dfrac n d} i [\gcd(i, \dfrac n d) == 1] (**)\\ \end{aligned} \]

由于 \(\sum_{i = 1}^n i [\gcd(i, n) == 1] = \dfrac {\varphi(n)n + [n == 1]}{2}\) 可知 \((**) = \sum_{d \mid n}\dfrac {\varphi(\dfrac n d)\dfrac n d + [n == d]}{2}\)

由于 \(d \mid n\) 亦可以化简为 \(\sum_{d \mid n}\dfrac {\varphi(d)d + [d == 1]}{2}\) ,可以记 \(f(n) = \sum_{d \mid n}\dfrac {\varphi(d)d + [d == 1]}{2}\) .

由于 \(f(n)\) 不是积性函数,但可以变换成如下形式

\[f(n) = \sum_{d \mid n}\dfrac {\varphi(d)d + [d == 1]}{2} = \dfrac 1 2 (\sum_{d \mid n}\varphi(d)d + [d == 1]) = \dfrac 1 2 \sum_{d \mid n}\varphi(d)d + 1 \]

\(g(n) = \sum_{d \mid n} \varphi(d)d\)​ ,易知 \(g(n)\)​ 为积性函数,可以通过 \(Dirichlet\)​ 前缀和 \(O(n\log \log n)\)​ 维护出来

再带入原式即可。

AC Code

#include <bits/stdc++.h>
#define int long long
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define debug(x) cout << x << endl;

using namespace std;

const int N = 4e6; 
const int M = 4e6 + 10;
const ll inf = 0x7f7f7f7f;
const ll INF = 1e7 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-7;
const double PI = acos(-1);

template <class T>
inline void read(T &x)
{
	x = (T)0;
	T tag = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-')
			tag = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}
	x *= tag;
}

int n, tot;
int phi[N + 10], vis[N + 10], prime[N + 10], h[N + 10];

void init()  
{
	phi[1] = 1;
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
		{
			prime[++tot] = i;
			phi[i] = i - 1;
		}
		for (int j = 1; j <= tot && prime[j] <= N / i; j++) 
		{
            vis[prime[j] * i] = 1;
            if (i % prime[j])
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            else {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
        }
	}
	for (int i = 1; i <= N; i++)
		h[i] = phi[i] * i;
}

signed main()
{
	init();
    for(int i = 1; i <= tot && prime[i] <= N; ++ i) 
        for(int j = 1; j * prime[i] <= N; ++ j) 
            h[j * prime[i]] += h[j];  
	int T;
	read(T);
	while (T--)
	{
		read(n);
		int ans = h[n] / 2 + 1;
		cout << ans << endl;
	}
	return 0;
}

后记

可能对于大佬来说这道题很显而易见,但是作为一个花费了两个晚上才搞懂的笱蒻来说确实有很多地方值得注意

首先是在处理 \(\gcd(i, n)\) 时,作为分母可能处理起来比较棘手,从一些题的经验来看,往往是通过枚举每一个 \(\gcd(i, n)\) 的值下手。之后就是常规的一些操作了。

其次是欧拉函数的一个推论很关键,其实就是自己不知道罢了qwq ,也就是 \(\sum_{i = 1}^n i [\gcd(i, n) == 1] = \dfrac {\varphi(n)n + [n == 1]}{2}\) .

看来数论的好多公式都要了解啊 \(qwq\)

posted @ 2021-09-14 22:26  circletime  阅读(72)  评论(0)    收藏  举报