目录
一. 问题描述
Problem Description
给出n个数字的序列,求表达式:
其中fac(i,j)是指区间[i,j]内数字乘积的素数因子的个数(重复算一个)。
二. 题解代码
因为1<=n<=1e6且1<=ai<=1e6,所以直接算乘积是不行的,a*b*c*d*...的素数因子和单独计算每一个的素数因子取交集是一样的。因为暴力是肯定不行的,所以我们需要计算每一个素数因子在每个区间里面的贡献。
因为枚举每个数字复杂度为O(n),唯一分解定理数出素因子复杂度为O(logn),所以nlogn的复杂度是可以接受的。对于某个位置 i 处的数字,假设其某个素因子在位置 i 之前最近出现过的位置为 j ,则位置 i 处的数字的素因子对整个结果的贡献为:
(1)首先从 [i,n],该素因子被贡献(n - i + 1)次;
(2)在 j+1 处开始一直到 i,[j+1,n]、[j+2,n]、...[i,n]都是出现 i 处的该素因子,所以共贡献了:(i - j)*(n - i + 1) 次该素因子在 i 处时的贡献;
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000000 + 7;
LL n,Num[maxn],prime[maxn],tot,PrimeIndex[maxn];
bool judge[maxn];
void init(){
tot = 0;
memset(judge,0,sizeof(judge));
judge[1] = 1;
for(int i = 2;i<maxn;i++){
if(!judge[i])prime[tot++] = i;
for(int j = 0;j<tot;j++){
if(i*prime[j]>=maxn)break;
judge[i*prime[j]] = 1;
if(i%prime[j]==0)break;
}
}
}
int main()
{
init();
memset(PrimeIndex,0,sizeof(PrimeIndex));
scanf("%d",&n);
for(int i = 1;i<=n;i++)scanf("%lld",&Num[i]);
LL sum = 0;
for(int i = 1;i<=n;i++){
LL m = Num[i];
for(int j = 0;j<tot&&prime[j]*prime[j]<=m;j++){
if(m%prime[j]==0){
sum+=(n - i + 1)*(i - PrimeIndex[prime[j]]);
PrimeIndex[prime[j]] = i;
while(m%prime[j]==0)m/=prime[j];
}
}
if(m > 1){
sum+=(n - i + 1)*(i - PrimeIndex[m]);
PrimeIndex[m] = i;
}
}
printf("%lld\n",sum);
}