题目:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1575
题意:
有
T
组数据,每组数据给出一个正整数
T≤10,n≤109
。
题解:
观察到
j
和
注意到
∑j≤i[gcd(i,j)=d]=∑jd≤id[gcd(id,jd)=1]=φ(id)
,所以我们可以对原式进行初步化简:
为了消除
d1
和
d2
相互的限制,我们需要将
lcm(d1,d2)
拆开。
对于一般情况,不难得到
lcm(n,m)=∑d[gcd(nd,md)=1]ndmdd=∑d|n,d|m∑d′|nd,d′|mdμ(d′)d′2ndd′mdd′d
,所以我们可以尝试进行代换:
在上面的最后一步,终于将
j
和
其中 ∗ 表示狄利克雷卷积。
因此所求是一个积性函数的前缀和,不妨设 f=id∗id2μ∗(id∗φ)2 ,可以发现 f(pk)=pk−1((2k+1)φ(pk+1)+1)(k>0) ,或者是 f(pk)=p2f(pk−1)+φ(pk)(2pk−1)(k>0) ,可以尝试利用筛法来计算答案。
这里首先给出
f(pk)
的复杂推导,令
g=id∗φ,h=id∗id2μ
,不难得到
g(pk)=(k+1)pk−kpk−1(k>0)
和
h(1)=1,h(pk)=pk(1−p)(k>1)
,则有
由于 −i2+(i+3)(3i+1)−(i+1)(3i+5)+(i+2)2=0 ,故只需考虑 i=−1,k−2,k−1,k 时的系数。现在考虑上和式外面的 g2(pk) ,则有
故 f(pk)=(2k+1)(p2k−p2k−1)+pk−1=pk−1((2k+1)φ(pk+1)+1) ,但是这样做非常麻烦,不妨回到原式来做如下推导
相对简单许多。
由于本题模数固定,且
N=max(n)
不是很大,所以我采取了分段打表的做法。比如要计算
f(x)(x∈[L,R])
的值,可以先筛出不超过
R−−√
的质数,再枚举每个质数去计算对
f(x)(x∈[L,R])
的贡献,这样做的复杂度是
O(∑Rx=LΩ(x)+R√lnR)=O((R−L+1)loglogR+R√lnR)
,选取一个恰当的阈值
K
,分别计算
显然
K
越小越好,但这样会增加代码的长度,需要具体问题具体分析。对于本题,所存储的值不超过
假设可以用
M
个字节存表,则
代码:(略去打表相关的代码)
#include <cmath>
#include <stdio.h>
#include <algorithm>
const int maxn = 31623, delta = 100000, maxd = 10001, maxl = delta + 1;
int tot, pr[maxn], d[maxn], tf[maxd];
int solve(int L, int R) // [L, R]
{
if(L > R)
return 0;
static int _val[maxl], _rem[maxl];
int *val = _val - L, *rem = _rem - L, lim = (int)ceil(sqrt(R));
for(int i = L; i <= R; ++i)
{
val[i] = 1;
rem[i] = i;
}
for(int i = 0; i < tot && pr[i] <= lim; ++i)
for(int j = R / pr[i] * pr[i]; j >= L; j -= pr[i])
{
rem[j] /= pr[i];
int cnt = 1, pk = pr[i];
for( ; rem[j] >= maxn && rem[j] % pr[i] == 0; rem[j] /= pr[i], ++cnt, pk *= pr[i], val[j] *= pr[i]);
if(rem[j] < maxn)
for( ; d[rem[j]] == pr[i]; rem[j] /= pr[i], ++cnt, pk *= pr[i], val[j] *= pr[i]);
val[j] *= (cnt << 1 | 1) * (pr[i] - 1) * pk + 1;
}
int ret = 0;
for(int i = L; i <= R; ++i)
{
if(rem[i] > 1)
val[i] *= 3 * (rem[i] - 1) * rem[i] + 1;
ret += val[i];
}
return ret;
}
inline int solve(int n) // [1, n]
{
int idx = n / delta, low = idx * delta, upp = low + delta;
return n - low <= upp - n ? tf[idx] + solve(low + 1, n) : tf[idx + 1] - solve(n + 1, upp);
}
int main()
{
for(int i = 2; i < maxn; ++i)
{
if(!d[i])
pr[tot++] = d[i] = i;
for(int j = 0, k; (k = i * pr[j]) < maxn; ++j)
{
d[k] = pr[j];
if(d[i] == pr[j])
break;
}
}
// parse table data from string to array, satisfied that tf[i] = solve(1, i * delta).
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
printf("%u\n", solve(n));
}
return 0;
}