分解质因数 - Prime Game - ICPC 2018 南京
题意:
给 定 一 个 长 度 为 n 的 整 数 序 列 a i 。 给定一个长度为n的整数序列a_i。 给定一个长度为n的整数序列ai。
令 令 令
m u l ( l , r ) = ∏ i = l r a i , f a c ( l , r ) 表 示 m u l ( l , r ) 本 质 不 同 的 质 因 子 的 个 数 mul(l,r)=\prod_{i=l}^ra_i,fac(l,r)表示mul(l,r)本质不同的质因子的个数 mul(l,r)=i=l∏rai,fac(l,r)表示mul(l,r)本质不同的质因子的个数
请 计 算 : 请计算: 请计算:
∑ i = 1 n ∑ j = i n f a c ( i , j ) \sum_{i=1}^{n}\sum_{j=i}^nfac(i,j) i=1∑nj=i∑nfac(i,j)
输入:
首 行 输 入 一 个 正 整 数 n ( 1 ≤ n ≤ 1 0 6 ) , 表 示 序 列 的 长 度 。 首行输入一个正整数n(1\le n \le 10^6 ),表示序列的长度。 首行输入一个正整数n(1≤n≤106),表示序列的长度。
第 二 行 包 含 n 个 整 数 a i ( 1 ≤ i ≤ n , 1 ≤ a i ≤ 1 0 6 ) , 为 这 个 整 数 序 列 。 第二行包含n个整数a_i(1\le i\le n,1\le a_i\le 10^6),为这个整数序列。 第二行包含n个整数ai(1≤i≤n,1≤ai≤106),为这个整数序列。
输出:
输 出 一 个 整 数 表 示 答 案 。 输出一个整数表示答案。 输出一个整数表示答案。
Examples
standard input
10
99 62 10 47 53 9 83 33 15 24
standard output
248
standard input
10
6 7 5 5 4 9 9 1 8 12
standard output
134
分析:
正 难 则 反 , 考 虑 每 个 质 因 数 对 每 个 区 间 的 贡 献 。 正难则反,考虑每个质因数对每个区间的贡献。 正难则反,考虑每个质因数对每个区间的贡献。
因 为 一 个 区 间 中 可 能 有 多 个 相 同 的 质 因 子 , 因为一个区间中可能有多个相同的质因子, 因为一个区间中可能有多个相同的质因子,
本 题 难 点 在 于 , 如 何 用 一 个 合 理 的 计 算 方 法 , 剔 除 掉 同 一 个 质 因 子 对 区 间 的 重 复 贡 献 。 本题难点在于,如何用一个合理的计算方法,剔除掉同一个质因子对区间的重复贡献。 本题难点在于,如何用一个合理的计算方法,剔除掉同一个质因子对区间的重复贡献。
思路如下:
① 、 首 先 , 我 们 对 每 个 数 分 解 质 因 数 , 对 于 每 一 个 质 因 数 p , 记 录 下 包 含 质 因 子 p 的 所 有 数 a i 的 位 置 i 。 ①、首先,我们对每个数分解质因数,对于每一个质因数p,记录下包含质因子p的所有数a_i的位置i。 ①、首先,我们对每个数分解质因数,对于每一个质因数p,记录下包含质因子p的所有数ai的位置i。
② 、 接 着 , 对 于 每 一 个 质 因 子 p , 我 们 遍 历 位 置 数 组 p o s , p o s [ p ] [ j ] 表 示 质 因 子 p 第 j 次 出 现 的 位 置 。 ②、接着,对于每一个质因子p,我们遍历位置数组pos,pos[p][j]表示质因子p第j次出现的位置。 ②、接着,对于每一个质因子p,我们遍历位置数组pos,pos[p][j]表示质因子p第j次出现的位置。
③ 、 对 于 每 一 个 位 置 p o s [ p ] [ j ] , 我 们 计 算 这 个 位 置 的 数 对 答 案 的 贡 献 , ③、对于每一个位置pos[p][j],我们计算这个位置的数对答案的贡献, ③、对于每一个位置pos[p][j],我们计算这个位置的数对答案的贡献,
Ⅰ 、 从 这 个 位 置 向 左 ( 包 含 这 个 位 置 ) , 剔 除 掉 上 一 个 位 置 p o s [ p ] [ j − 1 ] , \qquad Ⅰ、从这个位置向左(包含这个位置),剔除掉上一个位置pos[p][j-1], Ⅰ、从这个位置向左(包含这个位置),剔除掉上一个位置pos[p][j−1],
我 们 共 有 p o s [ p ] [ j ] − p o s [ p ] [ j − 1 ] 个 不 同 的 选 择 , 即 不 同 的 区 间 左 端 点 \qquad 我们共有pos[p][j]-pos[p][j-1]个不同的选择,即不同的区间左端点 我们共有pos[p][j]−pos[p][j−1]个不同的选择,即不同的区间左端点
Ⅱ 、 从 这 个 位 置 向 右 , 一 直 到 末 尾 n , \qquad Ⅱ、从这个位置向右,一直到末尾n, Ⅱ、从这个位置向右,一直到末尾n,
我 们 共 有 n − p o s [ p ] [ j ] + 1 种 不 同 的 选 择 , 即 不 同 的 区 间 右 端 点 。 \qquad 我们共有n-pos[p][j]+1种不同的选择,即不同的区间右端点。 我们共有n−pos[p][j]+1种不同的选择,即不同的区间右端点。
仔 细 思 考 这 个 策 略 , 计 算 每 个 位 置 的 数 的 贡 献 时 , 仔细思考这个策略,计算每个位置的数的贡献时, 仔细思考这个策略,计算每个位置的数的贡献时,
我 们 总 是 剔 除 了 前 一 个 位 置 的 具 有 相 同 质 因 子 的 数 的 影 响 。 我们总是剔除了前一个位置的具有相同质因子的数的影响。 我们总是剔除了前一个位置的具有相同质因子的数的影响。
我 们 不 需 要 考 虑 后 面 的 位 置 的 数 的 影 响 , 因 为 我 们 统 计 的 是 包 含 当 前 位 置 的 数 的 区 间 , 这 部 分 区 间 的 贡 献 。 我们不需要考虑后面的位置的数的影响,因为我们统计的是包含当前位置的数的区间,这部分区间的贡献。 我们不需要考虑后面的位置的数的影响,因为我们统计的是包含当前位置的数的区间,这部分区间的贡献。
而 因 为 前 面 的 数 也 未 考 虑 后 面 的 数 对 其 贡 献 的 影 响 , 所 以 我 们 要 剔 除 它 。 而因为前面的数也未考虑后面的数对其贡献的影响,所以我们要剔除它。 而因为前面的数也未考虑后面的数对其贡献的影响,所以我们要剔除它。
故 最 终 答 案 为 : 故最终答案为: 故最终答案为:
A n s = ∑ i = 0 p i ≤ m a x p ∑ j = 0 p o s [ p ] . s i z e ( ) [ p o s [ p ] [ j ] − ( j ? p o s [ p ] [ j − 1 ] : 0 ) ] × ( n − p o s [ p ] [ j ] + 1 ) Ans=\sum_{i=0}^{p_i\le max_p}\sum_{j=0}^{pos[p].size()}[\ pos[p][j]-(j\ ?\ pos[p][j-1]\ :\ 0)\ ]×(n-pos[p][j]+1) Ans=i=0∑pi≤maxpj=0∑pos[p].size()[ pos[p][j]−(j ? pos[p][j−1] : 0) ]×(n−pos[p][j]+1)
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const double eps = 1e-5;
const int mod = 1e9+7;
const int N = 1e6+10;
#define P pair<double,double>
#define x first
#define y second
#define ll long long
int n;
int primes[N], cnt;
bool st[N];
int num[N];
vector<int> pos[N];
int maxp;
void get_prime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]*i<=n;j++)
{
st[i*primes[j]]=true;
if(i%primes[j]==0) break;
}
}
}
void solve(int id)
{
int x = num[id];
int sqx = sqrt(x);
for(int i=0;primes[i]<=sqx;i++)
{
int p = primes[i];
if(x%p==0)
{
maxp = max(maxp,p);
pos[p].push_back(id);
while(x%p==0) x/=p;
}
}
if(x>1)
{
maxp=max(maxp,x);
pos[x].push_back(id);
}
}
ll cal()
{
ll res = 0;
for(int i=0;primes[i]<=maxp;i++)
{
int p = primes[i];
for(int j=0;j<pos[p].size();j++)
res += (ll)(pos[p][j] - (j ? pos[p][j-1] : 0)) * (n - pos[p][j] + 1);
}
return res;
}
int main()
{
get_prime(N-1);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&num[i]);
for(int i=1;i<=n;i++) solve(i);
printf("%lld\n",cal());
return 0;
}