[Gym-101981J] Prime Game (组合计数)

Posted tianwell

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Gym-101981J] Prime Game (组合计数)相关的知识,希望对你有一定的参考价值。

 

技术图片

 

题意:求for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) sum += f[i][j]; f[i][j]表示在序列从 i 位乘到第 j 位所形成的新的数的 不同质因子的个数.

思路:说是话,拿到题还是一开始想着能不能进行递推,比如先将每一个数进行 质因分解 然后用set不断更新统计个数来求和。但这样无论怎样都无法优化 (n^2) ,所以换思路再想。

就忽然想到了以前有一道做过的原题,题意是:给定一个长度为n的序列,然后求出每一个子区间不同数的个数和。而这一道题就是组合计数,计算每一个位置上的值对最后区间计数所产生的贡献。

而这道题类似,略微修改即是每一个位置上不只一个值(分解可能得到多个质因子),所以就用set来存放。

技术图片

上面这幅图,坐标轴上表示坐标,坐标轴下表示每个位置set所存放对应数的质因子数。比如我们对所有位置的5的贡献值进行计算。

位置坐标2:由于这个5要产生贡献,即其左边起始下标要从 1 开始,直到它本身的坐标 2. 其右边的开始坐标从它本身开始 2 一直延申到坐标轴右端. 所以 左 x 右 = (2-0) x (7-2+1) = 10;

位置坐标4:要使这个位置的5产生贡献,由于我们对上一个5右边所有区间进行了计算,所以在计算这个5的贡献的时候我们要从它上一个5的位置的下一位开始到它本身,即从 3-4,而其右端也是延申到右端点,所以 (4-2) x (7-4+1) = 8;

所以计算某个数的贡献,即 其左边第一个出现的下一位开始到他本身位置为左端点取值范围,从他本身开始到坐标轴右端点为其区间右端点取值范围,而左乘以右则为所有区间贡献总和。

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(0); cin.tie(0);
#define mp make_pair
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double Pi = acos(-1.0);
const double esp = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+7;
const int maxm = 1e6+7;
const int mod = 1e9+7;
//所有模板默认 prime[],powe[]从下标0开始取
const int MAXL = 1e6+7;
//int phi[MAXL];//欧拉函数
int tot;
int prim[MAXL];//素数表
int cnt;
int vis[MAXL];
//int powe[MAXL];//质数幂
int cur;
int pp[maxm];

set<int>qq[maxm];
set<int>::iterator it;
int a[maxm];
int getPrime(){
    int i ,j;
    int cnt = 0;
    memset(vis,0,sizeof(0));
    vis[0] = vis[1] = 1;
    for(i=2;i<=MAXL;++i)
    {
        if(!vis[i]) vis[i]= prim[cnt++]= i;
        for(j=0;j<cnt&&i*prim[j]<=MAXL;++j){
            vis[i*prim[j]] = 1;
            if(i%prim[j]==0) break;
        }
    }
    return cnt;
}
int getfac(int n,int cnt,int pos){
    for(int i=0;prim[i]*prim[i]<=n;i++){
        if(n%prim[i]==0) {
            qq[pos].insert(prim[i]);
            pp[prim[i]] = 0;
            while(n%prim[i]==0) n /= prim[i];
        }   
    }
    if(n>1) qq[pos].insert(n);
}
int main(){
    int n;
    scanf("%d",&n);
    cnt = getPrime();
    for(int i=1;i<=n;i++){
        scanf("%d",a+i);
        getfac(a[i],cnt,i);
    }
    ll sum = 0;
    for(int i=1;i<=n;i++){
        for(it = qq[i].begin();it != qq[i].end(); it++){
            int tmp = *it;
            int k = pp[tmp];
            pp[tmp] = i;
            sum += (ll) (i-k)*(n-i+1);
        }
    }
    printf("%lld
",sum);

} 

 

以上是关于[Gym-101981J] Prime Game (组合计数)的主要内容,如果未能解决你的问题,请参考以下文章

Gym - 101981M:(南京) Mediocre String Problem(回文树+exkmp)

Gym - 101981D Country Meow(模拟退火)

J - Prime Game锛堟暟璁哄垎瑙h川鍥犲瓙)

Gym - 101981M The 2018 ICPC Asia Nanjing Regional Contest M.Mediocre String Problem Manacher+扩增KMP(示

2018南京icpc-J-Prime Game (欧拉筛+唯一分解定理)

2018-2019 ACM-ICPC, Asia Nanjing Regional Contest(J Prime Game)