数论-分解质因数-POJ3421-X-factor Chains
题目:
输入正整数 X,求 X 的大于 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度,以及满足最大长度的序列的个数。
输入格式
输入包含多组数据,每组数据占一行,包含一个正整数表示 X。
输出格式
对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。
每个结果占一行。
数据范围
1≤X≤220
输入样例:
2
3
4
10
100
输出样例:
1 1
1 1
2 1
2 2
4 6
题意:
对给定正整数X,将其分解成 p1×p2×...×pn的形式,求从序列p的子序列a中,满足ai∣ai+1,i>=1,且严格单调递增的序列的最大长度是多少,以及最大长度的子序列的个数。
题解:
由唯一分解定理,任意正整数X,都能够分解成几个质因子的幂的积的形式。即X=p1a1×p2a2×...×pnan。
结论一: 约数个数为:∏i=1n(ai+1)=(a1+1)(a2+1)...(an+1)。
理解:X的因子可以表示为 p1b1×p2b2×...×pnbn,其中0<=bi<=ai,1<=i<=n,对X的幂次n元组(b1,b2,...,bn),每一个bi都有ai+1种取值,所以共有∏i=1n(ai+1)个约数。
结论二: 约数之和:∏i=1n∑j=0aipj=(p0+p1+...+pa1)(p0+...+pa2)...(p0+...+pan)。
理解:式子展开即所有因数之和。
回到本题:
对于X=p1a1×p2a2×...×pnan,对n元组(b1,b2,...,bn),0<=bi<=ai,最长的递增子序列中的每一项xi一定是在前一项xi−1的基础上乘上pi的一次方,i∈[1,n]。从1递增到(a1+a2+...+an),最大长度即∑i=1nai。
反应到n元组(b1,b2,...,bn)上:假设xi−1=p1b1×p2b2×...×pnbn,则xi=p1b1×p2b2×...×pjbj+1...×pnbn。也就是说xi在xi−1的基础上乘上了一个质因子 pj,(1<=j<=n),且bj+1<=aj。
因为质因子xi>1,从x0=(0,0,...,0)开始,x1在x0的基础上选择,共有a1+a2+...an个质因子,则x1的n元组共有(a1+a2+...+an)种选择。同理,x2就在x1的基础上选择,在x1的基础上增加"1",就比x1少一种可能。最终总共的选择方案就是(a1+a2+...+an)!种可能。
Eg:
举个例子:对X=180=22×32×5而言,有序列:2、22、22×3、22×3×5、22×32×5,长度为2+2+1=5。
反应到3元组(b1,b2,b3)上来:它们的取值对应为(1,0,0)、(2,0,0)、(2,1,0)、(2,1,1)、(2,2,1),就是每一项在前一项的基础上加上了"1",这个"1"可以加在三个位置的bi上,只要bi+1<=ai。
对于序列的第一个数字,三个位置5个质因子共有a1+a2+a3=2+2+1=5种取法,则第二个数字只能在其基础上增加1,共有4种取法...。因此共有5!种取法。
注意:
由于在选择过程中,同一质因子在同一位置会重复被选择,例如:因子2有2个,(1,0,0)被计算选择了2次,因子3也被重复计算选择了两次,那么重复部分相同的不同序列就有2!×2!=4次,最后要除去重复的可能。
Eg:
对序列:2、22、22×3、22×3×5、22×32×5,第一项有两个质因子2,故有2种选择,第三项和第四项的3可以调换位置,也有两种选择,那么该序列就被计算了2×2=4次。
同理,对每一种不同的排列,都会存在重复的计算,次数为a1!×a2!×a3!。
故最终总数为a1!a2!a3!(a1+a2+a3)!=30种。
结论:
推广至一般情况,总方案数:∏j=1naj!(∑i=1nai)!=a1!a2!...an!(a1+a2+...+an)!,同时最大长度∑i=1nai。
解题步骤:
- 线性筛筛出X的所有最小质因子。
- 计算每个质因子的幂。
- 计算最终答案
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N=(1<<20)+10;
int primes[N],cnt;
int minp[N];
bool st[N];
void get_prime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i])
{
minp[i]=i;
primes[cnt++]=i;
}
for(int j=0; primes[j]*i<=n;j++)
{
int tmp=primes[j]*i;
st[tmp]=true;
minp[tmp]=primes[j];
if(i%primes[j]==0) break;
}
}
}
int main()
{
get_prime(N);
int index[30];
int x;
while(~scanf("%d",&x))
{
memset(index,0,sizeof(index));
int k=1;
int tot=0;
while(x>1)
{
int p=minp[x];
while(x%p==0)
{
x/=p;
index[k]++;
tot++;
}
k++;
}
ll ans=1;
for(int i=1;i<=tot;i++) ans*=i;
for(int i=1;i<=k;i++)
for(int j=1;j<=index[i];j++)
ans/=j;
printf("%d %lld\n",tot,ans);
}
return 0;
}