洛谷P2303 [SDOI2012] Longge 的问题 题解
题目链接:P2303 [SDOI2012] Longge 的问题
题意:
给定一个整数 $n$,你需要求出 $\sum\limits_{i=1}^n \gcd(i, n)$ 。
输入格式:
输入只有一行一个整数,表示 $n$。
输出格式:
输出一行一个整数表示答案。
数据范围:
$1\leq n< 2^{32}$。
根据数学题套路,我们枚举 $\gcd(i,n)$ 的值。
这个时候其实直接 $\mathcal{O}\left(d(n)\times\sqrt{n}\right)$ 算就可以了,但还可以更快。
考虑把 $\varphi(n)$ 拆开(不妨将 $n$ 唯一分解为 $\prod_{1 \le i \le m} p_i^{k_i}$ )
可以发现有很多的 $d$ 有着相同的贡献。
考虑枚举 $[m]$ 的子集 $S$ ,它对应的 $d$ 的个数为 $\prod_{i \in S}k_i$ ,则它的贡献为
现在我们要求的是所有子集的和,因此我们考虑每个 $p_i$ 的贡献。
先讲一种通俗的题解方法。如下。
不妨记其贡献为 $\chi_i = k_i\frac{p_i-1}{p_i}$ ,可以发现答案是这么贡献的(例如 $m=3$ 时)
后面的式子可以化简为 $\prod(\chi_i+1)$ ,因此答案为
当然这不是一道观察题,其实有更数学的理解,只不过比较难懂。
因为每个 $p_i$ 的贡献是相互独立的,所以考虑只要每个 $p_i$ 是否被取。
如果取了,它的贡献就是在原来的答案上乘上一个 $\chi_i$ ,否则乘上一个 $e=1$ (单位元)
因此答案为
时间复杂度 $\mathcal{O}(\sqrt{n})$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)())
int n,t,k,res=1;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n; t = n; // t 用于算 p_i^{-1}
for(int i=2; i * i <= n; i++)
if(n % i == 0)
{
t /= i; k = 0;
while(n % i == 0) { ++k; n /= i; }
res = res * (k * (i - 1) + i);
}
if(n != 1) { t /= n; res = res * (2 * n - 1); }
cout << res * t << '\n';
return 0;
}
其实还有另一种做法。回到这一步
其等价于(其中 $*$ 表示狄利克雷卷积)
则原函数为积性函数。所以我们可以把原函数 $f(n)$ 分解为 $\prod f(p_i^{k_i})$ 。
对于每个形如 $f(p^c)$ 的式子,可以发现
而 $\varphi(p_i^c) = p^c - p^{c-1}$ 是个经典结论(证明可以直接套 $\varphi$ 展开公式,得 $p^c(1-p)$ )
因此有
因此答案为
时间复杂度 $\mathcal{O}(\sqrt{n})$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)())
int n,k,pwd,res=1;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i=2; i * i <= n; i++)
if(n % i == 0)
{
k = 0; pwd = 1;
while(n % i == 0) { ++k; pwd *= i; n /= i; }
res = res * ((k + 1) * pwd - k * pwd / i);
}
if(n != 1) { res = res * (2 * n - 1); }
cout << res << '\n';
return 0;
}
参考文献:
[1] https://www.cnblogs.com/PinkRabbit/p/8278728.html