【JZOJ4872】太阳神 题解

题目大意

  求这玩意儿:

  $n \le 10^{10}$

【首先】

  显然算出 $lcm<=n$ 的数对的数量,然后用 $n^2$ 去减。以下 $ans$ 表示的都是小于等于的数量。

【20%】n<=2000

  暴力。

【40%】n<=10^7

【60%】n<=10^8

  根号下分块套分块,时间我不会算,大概是非常不满的三个根号,反正不能过就是了。

【100%】n<=10^10

  设 $m=\lfloor\frac{n}{D^2d}\rfloor$,则上述式子的最后一个sigma实际上是在求 $1$~$m$ 的约数个数和。这个东西在合理范围内是可以预处理的。
  因此对于最后一个sigma,如果 $m \le 10^7$,那么我们直接用预处理的结果,否则再根号算。由于上限下降很快,所以要用根号算的其实很少。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<cmath>
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const LL mo=1e9+7, maxn=1e7+5;

LL n;

int p0,p[maxn],miu[maxn],fy[maxn],fy1[maxn];
LL Sfy[maxn];
bool bz[maxn];
void Pre(int n)
{
miu[1]=fy[1]=1;
fo(i,2,n)
{
if (!bz[i])
{
p[++p0]=i;
miu[i]=-1;
fy[i]=2, fy1[i]=1;
}
fo(j,1,p0)
{
if ((LL)i*p[j]>n) break;
bz[i*p[j]]=1;
if (i%p[j]==0)
{
miu[i*p[j]]=0;
fy[i*p[j]]=fy[i]/(fy1[i]+1)*(fy1[i]+2);
fy1[i*p[j]]=fy1[i]+1;
break;
} else
{
miu[i*p[j]]=-miu[i];
fy[i*p[j]]=fy[i]*2;
fy1[i*p[j]]=1;
}
}
}
fo(i,1,n) Sfy[i]=(Sfy[i-1]+fy[i])%mo;
}

LL cala(LL x)
{
LL re=0;
for(LL ai=1, aj; ai<=x; ai=aj+1)
{
aj=x/(x/ai);
re=(re+x/ai*(aj-ai+1)%mo)%mo;
}
return re;
}

int main()
{
Pre(maxn-5);

scanf("%lld",&n);
LL sqrtn=sqrt(n);


LL ans=0;
for(LL D=1; D<=sqrtn; D++)
{
LL maxd=n/D/D, ansD=0;
for(LL di=1, dj; di<=maxd; di=dj+1)
{
dj=maxd/(maxd/di);
LL maxa=maxd/di;

LL t=(maxa<=maxn-5) ?Sfy[maxa] :cala(maxa) ;
ansD=(ansD+t*(dj-di+1)%mo)%mo;
}
ans=(ans+(ansD*miu[D]%mo+mo)%mo)%mo;
}

n%=mo;
printf("%lld\n",(n*n%mo-ans+mo)%mo);
}