分解质因数 - Prime Game - ICPC 2018 南京

分解质因数 - Prime Game - ICPC 2018 南京

题意:

给 定 一 个 长 度 为 n 的 整 数 序 列 a i 。 给定一个长度为n的整数序列a_i。 nai

令 令

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=lraifac(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=1nj=infac(i,j)

输入:

首 行 输 入 一 个 正 整 数 n ( 1 ≤ n ≤ 1 0 6 ) , 表 示 序 列 的 长 度 。 首行输入一个正整数n(1\le n \le 10^6 ),表示序列的长度。 n(1n106)

第 二 行 包 含 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),为这个整数序列。 nai(1in,1ai106)

输出:

输 出 一 个 整 数 表 示 答 案 。 输出一个整数表示答案。

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。 ppaii

② 、 接 着 , 对 于 每 一 个 质 因 子 p , 我 们 遍 历 位 置 数 组 p o s , p o s [ p ] [ j ] 表 示 质 因 子 p 第 j 次 出 现 的 位 置 。 ②、接着,对于每一个质因子p,我们遍历位置数组pos,pos[p][j]表示质因子p第j次出现的位置。 ppospos[p][j]pj

③ 、 对 于 每 一 个 位 置 p o s [ p ] [ j ] , 我 们 计 算 这 个 位 置 的 数 对 答 案 的 贡 献 , ③、对于每一个位置pos[p][j],我们计算这个位置的数对答案的贡献, pos[p][j]

Ⅰ 、 从 这 个 位 置 向 左 ( 包 含 这 个 位 置 ) , 剔 除 掉 上 一 个 位 置 p o s [ p ] [ j − 1 ] , \qquad Ⅰ、从这个位置向左(包含这个位置),剔除掉上一个位置pos[p][j-1], ()pos[p][j1],

我 们 共 有 p o s [ p ] [ j ] − p o s [ p ] [ j − 1 ] 个 不 同 的 选 择 , 即 不 同 的 区 间 左 端 点 \qquad 我们共有pos[p][j]-pos[p][j-1]个不同的选择,即不同的区间左端点 pos[p][j]pos[p][j1]

Ⅱ 、 从 这 个 位 置 向 右 , 一 直 到 末 尾 n , \qquad Ⅱ、从这个位置向右,一直到末尾n, n

我 们 共 有 n − p o s [ p ] [ j ] + 1 种 不 同 的 选 择 , 即 不 同 的 区 间 右 端 点 。 \qquad 我们共有n-pos[p][j]+1种不同的选择,即不同的区间右端点。 npos[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=0pimaxpj=0pos[p].size()[ pos[p][j](j ? pos[p][j1] : 0) ]×(npos[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值