AT5200 [AGC038C] LCMs

$\text{LuckyLeaves}$ 手把手教你入门莫反。

题目传送门以及原题地址

推式子:

首先有:

$\displaystyle\sum_{i=1}^{n}\sum_{j=i+1}^{n}\operatorname{lcm}(a_i,a_j)=\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\operatorname{lcm}(a_i,a_j)-\sum_{i}^{n}a_i$

这是显然的,而现在我们就将 $i,j$ 提到了同等地位。

因为有 $a_i\leq10^6$ ,那么,我们可以从值域方面来考虑,这里记 $cnt[i]$ 为 $a_j=i$ 的个数。

继续推式子:

然后使用提前思想,得到:

然后,就会发现(其实应该早就可以发现),$cnt[i]$ 已经具有了 $[dp|i]$ 的功能了。

所以再次化简式子(令 $N=1e6$):

然后设出 $T=pd$ :

再设:

则有:

有 $Mub[],Num[]$ 的预处理,时间复杂度为 $\mathcal O(n\ln n)$ ,然后枚举 $T$ 的时间复杂度为 $\mathcal O(n)$ ,则这道题的时间复杂度为 $\mathcal O(1e6\ln 1e6+1e6)=\mathcal O(7e6)$ 。

然后说一个调了很久的 $\text{Bug}$ ,因为答案是:

$\displaystyle ans=\frac{1}{2}\sum_{T}^{N}T\times Mub[T]\times Num[T]^2-\sum_{i}^{n}a_i$

因为是模意义下的运算,所以这里不能直接做除法,而是需要乘上逆元。

不过 $2$ 的逆元一向好求:$inv[2]=(Mod+1)/2$ 。

上述是以 $p$ 转换的,还可以保留 $d$ 消 $p$ 的做法,不过那样子做会有一个线性求逆元的过程,且众所周知,逆元容易出锅。

对于数学题而言,总是一个化繁而简的过程。

AC Code
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<iomanip>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#define gh() getchar()
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=gh(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=gh();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=gh();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1)
{
read(x),read(x1...);
}
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<class T>
inline bool checkMax(T &x,T &y)
{
return x<y?x=y,1:0;
}
template<class T>
inline bool checkMin(T &x,T &y)
{
return x>y?x=y,1:0;
}
const int MAXN=2e5+10;
const int MAXS=2e6+10;
const int Mod=998244353, inv=(Mod+1)/2;
int N,Tot,MaxNum,Sum;
int Cnt[MAXS],Num[MAXS],Mub[MAXS];
int Mu[MAXS],G[MAXS],Pri[MAXS];
bool Is[MAXS];
inline void sieve(int n)
{
for(int T=1;T<=n;++T)
for(int i=1;T*i<=n;++i)
Num[T]=(Num[T]+1ll*i*Cnt[i*T]%Mod)%Mod;
Mu[1]=Is[1]=1;
for(int i=2;i<=n;++i)
{
if(!Is[i]) Pri[++Tot]=i,Mu[i]=-1;
for(int j=1;j<=Tot&&i*Pri[j]<=n;++j)
{
Is[i*Pri[j]]=1;
if(i%Pri[j]==0)
{
Mu[i*Pri[j]]=0;
break;
}
Mu[i*Pri[j]]=-Mu[i];
}
}
for(int T=1;T<=n;++T)
for(int p=1;p*T<=n;++p)
Mub[T*p]=(Mub[T*p]+1ll*Mu[p]*p)%Mod;
}
inline int solve(int n)
{
int res=0;
for(int T=1;T<=n;++T)
res=(res+(ll)T*Mub[T]%Mod*Num[T]%Mod*Num[T]%Mod)%Mod;
return res;
}
signed main()
{
// freopen("inv.in","r",stdin);
// freopen("inv.out","w",stdout);
read(N);
for(int i=1,x;i<=N;++i) read(x),Sum=(Sum+x)%Mod,++Cnt[x],checkMax(MaxNum,x);
MaxNum=std::min(1000000,2*MaxNum);
sieve(MaxNum);Sum%=Mod;
int ans=solve(MaxNum);
ans=1ll*(ans-Sum)*inv%Mod;
write((ans+Mod)%Mod);
return 0;
}
/*
10
356822 296174 484500 710640 518322 888250 259161 609120 592348 713644
*/