【ICPC】2018南京站 J. Prime Game | 计数

195 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

【ICPC】2018南京站 J. Prime Game | 计数

题目链接

20182019-acmicpc-asia-nanjing-regional-contest-en.pdf (codeforces.com) Dashboard - 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest - Codeforces

题目

image.png

题目大意

给定 nn 个正整数 a1,a2,...,ana_1,a_2,...,a_n。定义

mul(l,r)=i=lraimul(l,r)=\prod_{i=l}^ra_i

定义 fac(l,r)fac(l,r) 表示 mul(l,r)mul(l,r) 中不同质因数的数量。

输出表达式

i=1nj=infac(i,j)\sum_{i=1}^n\sum_{j=i}^nfac(i,j)

的值。

思路

显然可以对每个数字分解质因数,然后对其每个质因数单独进行统计这个质因数在多少个区间里有贡献。我们开一个数组 fc[x]fc[x] 表示从开始遍历到当前位置,质数 xx 最后一次出现在 afc[x]a_{fc[x]} 里。

image.png

假设遍历到了位置 iiaia_i 有质因数 xx,我们知道 ii 前面所有的下标中, 最后一个有质因数 xx 的是 afc[x]a_{fc[x]}。为了避免重复计算,我们暂不考虑跨越了 ii 的区间,只考虑不跨越 ii 且跨越 fc[x]fc[x] 的区间。这样的区间有 fc[x]×(ifc[x])fc[x]\times(i-fc[x]) 个,则我们认为 afc[x]a_{fc[x]}xx 对答案的贡献为 fc[x]×(ifc[x])fc[x]\times(i-fc[x])。然后我们更新 fc[x]fc[x]

等到遍历完整个数组后,我们对于每个 xx,下标 fc[x]fc[x] 对答案的贡献都还没有计算,则我们遍历 fcfc 数组,每个 xx 对答案的贡献为 fc[x]×(n+1fc[x])fc[x]\times (n+1-fc[x])

代码

#include <stdio.h>
using namespace std;
using LL=long long;
const int N=1000001;
int n,a[N],vis[N],p[N],tot;
LL e[N];
int fc[N],m;
LL ans;
void split(int x)
{
	m=0;
	for (int i=1;i<=tot&&p[i]*p[i]<=x;++i)
	{
		if (x%p[i]) continue;
		fc[++m]=p[i];
		while (x%p[i]==0) x/=p[i];
	}
	if (x>1) fc[++m]=x;
}
int main()
{
	n=1e6;
	for (int i=2;i<=n;++i)
	{
		if (!vis[i]) p[++tot]=i;
		for (int j=1;j<=tot&&i*p[j]<=n;++j)
		{
			vis[i*p[j]]=1;
			if (i%p[j]==0) break; 
		}
	}
	scanf("%d",&n);
	for (int i=1,x;i<=n;++i)
	{
		scanf("%d",&x);
		split(x);
		for (int j=1;j<=m;++j)
		{
			if (e[fc[j]]) ans+=(i-e[fc[j]])*e[fc[j]];
			e[fc[j]]=i;
		}
	}
	for (int i=1;i<=1e6;++i)
		if (e[i]) ans+=(n-e[i]+1)*e[i];
	printf("%lld",ans);
        return 0;
}