The 2018 ACM-ICPC Asia Nanjing Regional Programming Contest J - Prime Game

[题目传送门](Attachments - 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest - Codeforces)


题目描述

the problem

题目解析

题目的意思实际上是求每个子区间的乘积总共有多少个不同的质因数

在这里需要用到贡献度的思想,具体可以参考这两篇文章:

Beauty of Array(贡献度思想)_pioneer1-CSDN博客

贡献思想 + 数论 + 思维(例题 Problem J. Prime Game)_qq_41818544的博客-CSDN博客

其实第二篇就是别人写的本题的题解

由于之前没有接触过贡献度的思想,所以本蒟蒻根本做不出这道题qwq

本题要求每个子区间的乘积总共有多少个不同的质因数,所以可以从单个元素入手,求出这个元素的质因数,然后确定有多少个区间包含了这个元素,之后记录这个元素在哪些区间中出现过,如果之前的元素的质因数和当前元素有相同的元素,那就不计入之前元素 存在的区间,如果有多个质因数,那就记录每个质因数的区间数量,最后得到区间的个数,就是这个元素的贡献度自己都 写糊涂了

接下来以样例2再现一下这个过程:

样例2中给了一个长度(n)为10的数组

序号(i) 1 2 3 4 5 6 7 8 9 10
元素(ai) 6 7 5 5 4 9 9 1 8 12
质因数(x) 2 7 5 5 2 3 3 0 2 2
3 3
  • *第一个元素的质因数 2 *,它能贡献的区间有[1,1], [1,2] ······[1,10] 共10个

    第一个元素的质因数3, 它能贡献的区间有[1,1], [1,2] ······[1,10] 共10个

    那么第一个元素的贡献为10 + 10 = 20

  • 第二个元素的质因数7 , 它能贡献的区间有[1,2],[1,3],……,[1,10] 和 [2,2],[2,3],……,[2,10] 共18个区间,那么第二个元素的贡献为9 + 9 = 18

  • 第三个元素的质因数5, 它所能贡献的区间[1, 3], [1, 4], ......, [1 10], [2, 3], [2, 4], ......., [2, 10], [3, 3], [3, 4], ......, [3, 10]共24个元素,那么第三个元素的贡献为8 + 8 + 8 = 24

    在第四个元素之前,前三个元素都是都没有重复的质因数,经过观察,我们不免能发现一个规律:*一个元素的质因数的贡献度 =(长度 + 1 - 当前元素的个数) * 序号 *(n + 1 - i) * i

  • 但是,第四个元素出现了和第三个元素相同的质因数,如果按照之前的规律,那么这个这个元素的质因数的贡献度为 28,也就是说该元素包括了 [1,4], [1,5], ...., [1, 10], [2, 4], [2,5], .....[2, 10], [3, 4], ......, [3, 10] , [4, 4], [4, 5], ...... ,[4, 10] 。但是,只有[4, 4], [4, 5], ...... ,[4, 10]这8个区间是之前元素的质因数没有贡献的区间,所以第四个元素的贡献度是8

    所以,这个元素的贡献度 = (n + 1 - i) * i - (n + 1 - l) * l (l是上次质因数出现的位置), 化简之后得到了最终的关系式: (n + 1 - l) * (i - last)(last 是上一次质因数出现的位置) 。这样一来,就算是基本解决这个问题了。

    AC代码

    #include<iostream>
    using namespace std;
    const int N = 1e6 + 10000;
    long long q[N];   //注意数据范围,防爆
    long long last[N];
    int n;
    
    int main(){
        ios::sync_with_stdio(false), cin.tie(0);
        long long ans = 0;//储存答案
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> q[i];
        for(int i = 1; i <= n; i++){
            long long x = q[i];
            for(int j = 2; j * j <= x; j++){
                if(x % j != 0) continue;
                while(x % j == 0) x /= j;//以上为求质因数
                ans += (n + 1 - i) * (i - last[j]);//记录答案
                last[j] = i;//储存质因数出现的位置
            }
            if(x != 1){ //处理最后的质因数
                ans += (n + 1 - i) * (i - last[x]);
                last[x] = i;
            }
        }
        cout << ans << endl;
    
        return 0;
    }

    这篇题解有些的地方阐述的不是很好,奈何本人表达能力有限,希望各位大佬能对这篇题解提出一些宝贵的建议,指出改进的地方