Acwing 1295. X的因子链 (唯一分解定理 线性筛 质因数分解 有重复数集合的排列数)

题目描述

原题链接

输入正整数 X,求 X 的大于 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度,以及满足最大长度的序列的个数。

输入格式

输入包含多组数据,每组数据占一行,包含一个正整数表示 X。

输出格式

对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。

每个结果占一行。

数据范围

1≤X≤220

输入样例:
2
3
4
10
100
输出样例:
1 1
1 1
2 1
2 2
4 6

分析

由唯一分解定理可知, 任意一个数 X X X都可以分解成 X = p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n X = p_1^{k_1}*p_2^{k_2}*...*p_n^{k_n} X=p1k1p2k2...pnkn(其中 p 1 < p 2 < . . . < p n , 且 p 1 , p 2 , . . . , p n 为 质 数 p_1<p_2<...<p_n,且p_1,p_2,...,p_n为质数 p1p2<...<pn,p1,p2,...,pn)
题目要求的序列为 X X X的大于 1 1 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度
由于序列是由 X X X的因子组成, 所以容易满足前一项整除后一项, 且严格递增


接下来考虑这样的序列的最大长度是多少?
观察 X = p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p n k n X = p_1^{k_1}*p_2^{k_2}*...*p_n^{k_n} X=p1k1p2k2...pnkn, 不难发现在满足整除和递增的条件下,
每次 后 一 项 = 前 一 项 ∗ p s ( p s 为 X 中 剩 余 的 最 小 的 质 因 子 ) 时 后一项 = 前一项 *p_s(p_s为X中剩余的最小的质因子)时 =ps(psX),得到的序列长度最长
(假如 p s p_s ps不是当前最小的质因子, 则 p s p_s ps一定可以再分解成 p m ∗ p n p_m*p_n pmpn, 则这样的序列长度并不是最长)


所以最长序列的长度为 k 1 + k 2 + . . . + k n k_1 + k_2 + ... + k_n k1+k2+...+kn, 个数为 ( k 1 + k 2 + . . . + k n ) ! k 1 ! k 2 ! . . k n ! \frac{(k_1 + k_2 + ... + kn)!}{k_1!k_2!..k_n!} k1!k2!..kn!(k1+k2+...+kn)!(有重复数集合的排列数)


对于每个数 X X X的最小质因子,我们可以用线性筛筛选出来

实现

#include <cstdio>
#include <iostream>
using namespace std;
const int N = (1 << 20) + 9;
int x;
int primes[N]; // 记录质数
int minp[N]; // 记录质因子
bool st[N]; // st[i] = 0 表示 i 是 质数
int cnt[N]; // 记录数x的每个质因子的数量
void getPrimes() // 筛出质数, 以及每个数的最小质因子
{
    int cnt = 0;
    for(int i=2; i<= 1 << 20; i++)
    {
        if(!st[i])
        {
            primes[cnt++] = i;
            minp[i] = i;
        }
        for(int j=0; i*primes[j] <= 1 << 20; j++)
        {
            st[i * primes[j]] = true;
            minp[i * primes[j]] = primes[j];
            if(i % primes[j] == 0) break;      
        }
    }
}
int main()
{
    getPrimes();
    while(cin >> x)
    {
        int k = 0; // 记录数x质因子p的数量
        int total = 0; // 记录数X质因子的个数
        long long ans = 1;

        // 分解质因子
        while(x > 1)
        {
            int p = minp[x];
            cnt[k] = 0; // 不要使用memset, 复杂度太高
            while(x % p == 0) 
            {
                x /= p;
                cnt[k]++;
                total++;
            }
            k++;
        }

        for(int i=1; i<=total; i++) ans *= i; // total的阶乘
        for(int i=0; i<k; i++) // 除以每个质因子数量的阶乘
        {
            for(int j=1; j<=cnt[i]; j++)
            {
                ans /= j;
            }
        }
        cout << total << " " << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值