1295. X的因子链----算术基本定理/多重集合排列数/欧拉筛

原题链接https://www.acwing.com/problem/content/1297/

算术基本定理

任何数都能质数积表示:

N=p_1^{d_1}+p_2^{d_2}+...+p_k^{d_k} \\
e.g.

12 = 2^2 \times 3 \\ 105 = 3\times5\times7 \\ 540 = 2^2\times 3^3\times 5

一个数的最长表示方法就是:质因子从小到大

多重集合的排列数问题

以上面例子的2^2\times 3^3\times5为例,问2个2和3个3和1个5有多少种排列数,首先我们可以知道总的组合数为6!个,将所有的组合分组,不同的组代表不同的组合,例如2 2 3 3 3 5 和 2 3 3 3 2 5就是不同的组,我们要求的排列数实际就是有多少个组。因为2和3有重复,所以每个组都有2!\times3!个重复的方案,即组的数量:

num = \frac{6!}{2!\times3!}

该公式可以推而广之,通用式我就不写了

欧拉筛(线性筛)O(n)

直接上代码看更清晰(这是普通的欧拉筛,本题的欧拉筛有改进增加了最小质因数的存储,可以往后看)

int prime[20000]; //存储素数的数组
int c=0; //记录prime中元素的个数(素数的个数)
bool isprime[20000]; //标记1-20000是不是素数1-是 0-不是
int n;
void EulerSevie()
{
	//遍历2-20000的所有数字,都判断一遍
	for(int i=2; i<=n; i++)
	{
		//是素数就入组
		if(!isprime[i])
			prime[++c]=i;
		//遍历在<=i的所有素数
		for(int j=1; j<=c&&i*prime[j]<=n; j++)
		{
			isprime[i*prime[j]]=true;//素数的倍数一定是合数 与埃氏筛不同的地方-埃氏是素数为外循环,所有数为内循环
			//EulerSeive是所有数为外循环,素数为内循环,这样能及时判断避免重复运算
			//如果prime[j]是i的因子,那么就认为prime[j]是i的最小质因子,就退出循环
			//因为欧拉筛的原理就是通过最小质因子把质数筛出去,每一个i%prime[j]==0中的i都是合数那么就起码存在一个及以上的素数是它的因子
			//我们只要计算最小的那个因子就可以,更大的在后面会被算(且是不能被省略的)那么前面的就多余了
			//例如i=9,在prime[2]=3时就应该break,如果没有就会让isprime[i*prime[j]]=isprime[9*5]=isprime[45]=true
			//但是在i=15时,在prime[2]时就会算到isprime[45],这是不可避免的但是在i=9时那个是可以省略的
			if(i%prime[j]==0)
				break;
		}
	}
}

本题分析

本题就是结合了上述三个数论,先利用欧拉筛得到素数的过程中,求得每个数的最小质因子,用minp数组存储。再利用算术基本定理将x分解成最长的质数组合,能分解多少个质数这个序列就有多长,再将这个序列进行组合,利用多重集合的排列数可以求出排列数也就是方案数。

AC Code

#include <iostream>
using namespace std;
const int N = (1<<20)+10;

/*
 @isprime[i],1-不是素数 0-是素数
 @primes[N] 存素数的数组
 @minp[i] i的最小质因子
*/
int isprime[N],primes[N],minp[N]; // 
int cnt;

void EulerSevie(int n)
{
    for(int i=2; i<=n; i++)
    {
        if(!isprime[i])
        {
            minp[i] = i; //质数的最小质因子就是自己
            primes[++cnt] = i;
        }
        for(int j=1; j<=cnt&&i*primes[j]<=n; j++)
        {
            int t=i*primes[j]; //t一定是合数 排除
            isprime[t]=true;
            minp[t]=primes[j]; //t的最小质因子
            if(i%primes[j]==0) //如果prime[j]是i的最小质因子那么就退出循环 避免后续重复计算i*primes[j]
                break;
        }
    }
}

int main()
{
    int x,sum[N];
    EulerSevie(N);
    while(cin>>x)
    {
        //求x的质因子
        int tot=0,k=0;
        while(x>1)
        {
            int p=minp[x];
            sum[k]=0;
            while(x%p==0)
            {
                x /= p;
                sum[k]++;
                tot++;
            }
            k++;
        }

        long long res=1;
        for(int i=1; i<=tot; i++)
            res *= i;
        for(int i=0; i<k; i++)
            for(int j=1; j<=sum[i]; j++)
                res /= j;
        cout<<tot<<" "<<res<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linengcs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值