【莫比乌斯反演】C - LCMs - AGC038

题目链接(https://atcoder.jp/contests/agc038/tasks/agc038_c)

Time Limit: 2 sec / Memory Limit: 1024 MB

Score : 700 points

Problem Statement

We have an integer sequence of length N: A0,A1,,AN1.Find the following sum (lcm(a,b) denotes the least common multiple of aand b):i=0N2j=i+1N1lcm(Ai,Aj)

Since the answer may be enormous, compute it modulo 998244353.

Constraints

1N200000

1Ai1000000

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N

A0 A1  AN1

Output

Print the sum modulo 998244353.

Sample Input 1

3 2 4 6

Sample Output 1

22

lcm(2,4)+lcm(2,6)+lcm(4,6)=4+6+12=22.

Sample Input 2

8 1 2 3 4 6 8 12 12

Sample Output 2

313

Sample Input 3

10 356822 296174 484500 710640 518322 888250 259161 609120 592348 713644

Sample Output 3

353891724

题意

给定长度为N的序列A1,A2,...,AN,求i=0N2j=i+1N1lcm(Ai.Aj),模998244353

(1N2105,1Ai106)

思路

(笔记)

众所周知lcm(a,b)==(ab)/gcd(a,b)

设答案为ans,则可以由i=0N1j=0N1lcm(Ai,Aj)=2ans+i=0NAi

间接知道ans大小

i=0N1j=0N1lcm(Ai,Aj)

=i=0N1j=0N1(AiAj)/gcd(Ai,Aj)

=d=1L1/di=0N1j=0N1(AiAj)[gcd(Ai,Aj)=d]

f(d)=i=0N1j=0N1(AiAj)[gcd(Ai,Aj)=d]

f(d)作倍数和,i,j相当于是一样的所以后面作平方

g(d)=d|df(d)=(i=0N1Ai[d|Ai])2

f(d)=d|dμ(d/d)g(d)=d|dμ(d/d)(i=0N1Ai[d|Ai])2

最终得到求和式

i=0N1j=0N1lcm(Ai,Aj)=d=1L1/dd|dμ(d/d)(i=0N1Ai[d|Ai])2

在这里,对于任意的d的这样的一个求和式h(d)=d|nf(d)g(n/d),可以通过枚举因子或者枚举倍数来求得h(n),复杂度O(nlogn)

然后就可以一步步求求和式:

对于求和式,第一项是求所有的Aid的倍数的求和, (i=0N1Ai[d|Ai]),就可以把这里的求和看作h(d)=i=0N1Ai[d|Ai],把Ai用一个数组存起来,对这个数组进行倍数和,可以用nlogn的复杂度求取h(d),用桶数组pos[]优化方便累加,即h(d)=d|iposi

然后就可以来求d|dμ(d/d)h(d)2,又是类似于倍数和的一种形式,可以用nlogn的复杂度求取d|dμ(d/d),即h(d)=d|dμ(d/d)这样

最后可以用O(n)的复杂度求取d=1L1/dh(d)

总复杂度O(LlogL+N)L=maxAi

(嘤嘤嘤,被笨死惹,不想敲注释,名字和思路里的变量都对应的上吧)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
#define endl '\n'
const int N = 1e6 + 100;
const int mod = 998244353;
const double pi = acos(-1.0);
typedef long long ll;
using namespace std;
int n;
int mx;
int a[N],pos[N];
int h1[N], h2[N];
int miu[N];
int prime[N], num;
bool vis[N];
int quickpow(int a, int b) {
	int res = 1;
	while (b > 0) {
		if (b & 1) res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res;
}
int getinv(int a) {
	return quickpow(a, mod - 2);
}
void init() {
	mx = 0;
	for (int i = 1; i <= n; i++) {
		a[i] = 0;
	}
	return;
}
void solve() {
	init();
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		pos[a[i]] = (pos[a[i]] + a[i]) % mod;
		mx = max(mx, a[i]);
	}

	miu[1] = 1;
	for (int i = 2; i <= 1e6; i++)
	{
		if (!vis[i]) prime[++num] = i, miu[i] = -1;
		for (int j = 1; j <= num && i * prime[j] <= 1e6; j++)
		{
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
			miu[i * prime[j]] = -miu[i];
		}
	}

	for (int d = 1; d <= 1e6; d++)
	{
		int d_inv = getinv(d);
		for (int dd = d; dd <= 1e6; dd += d)
		{
			h1[d] = (h1[d] + pos[dd]) % mod;
			h2[dd] = (h2[dd] + miu[dd / d] * d_inv % mod + mod) % mod;
		}
	}

	int ans = 0;
	for (int d = 1; d <= mx; d++) {
		ans = (ans + h1[d] * h1[d] % mod * h2[d] % mod) % mod;
	}

	ans = ((ans - h1[1]) % mod + mod) % mod;

	cout << ans * getinv(2) % mod;
	return;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	while (cin >> n) {
		solve();
	}
	return 0;
}
posted @   TomiokapEace  阅读(83)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
点击右上角即可分享
微信分享提示