问题描述
原题来自 POJ 3421
输入正整数 x,求 x 的大于 1 的因子组成的满足任意前一项都能整除后一项的序列的最大长度,以及满足最大长度的序列的个数。
输入
多组数据,每组数据一行,包含一个正整数 x。
输出
对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。
样例输入
2
3
4
10
100
样例输出
1 1
1 1
2 1
2 2
4 6
提示
数据范围与提示:
对于全部数据,1≤x≤220。
问题分析
多重集排列计数,唯一分解定理。
由x数链的性质可以发现,将n分解为其质因数相乘的形式后,数链中任何一个数必定是这些质因数的某个组合,且任意相邻的2个质因数中后面的数必定是在前面的数的质因数的组合的基础上再乘上另外的一个质因数。所以很容易发现,数链的长度就是其质因数的次数的总和,然后将质因数泛化成物品,质因数的次数为每类物品的数量,则最后的答案就是求这些物品的多重集总排列。套用公式即可!
首先要对x进行因子分解。这样可以得到总的因子个数c,不同的因子为f1,f2,...,fn其次方数分别为e1,e2,...,en。那么,不同序列的个数为c!/(e1!e2!......en!)。
算法设计
数组factorial[],若factorial[i]=k则k=i!,即每个元素存放其下标的阶乘值。初始化将阶乘算出来打表,避免重复计算。那个常量N取22是应为22!还能够存储在unsigned long long类型变量中,再大就已经无法存储了。变量fcount存储x因子的数量,变量ecount存储各个因子的次方数(重复次数)。
代码
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
const int N = 22;
ULL factorial[N+1] = {1};
void init(int n)
{
for(int i=1; i<=n; i++)
factorial[i] = factorial[i - 1] * i;
}
void solve(ULL x)
{
ULL fcount=0, denominator=1;
for(ULL i=2; i*i<=x; i++) {
if(x % i == 0) {
int ecount = 0;
while(x % i == 0) {
ecount++;
x /= i;
}
fcount += ecount;
denominator *= factorial[ecount];
}
}
if(x > 1)
fcount += 1;
cout << fcount << " " << factorial[fcount] / denominator << endl;
}
int main()
{
init(N);
ULL x;
while(cin >> x)
solve(x);
return 0;
}
算法复杂度分析
由solve()函数中,for(ULL i=2; i*i<=x; i++)可知,该处算法复杂度为O(n),由init()函数中,for(int i=1; i<=n; i++)可知,该处算法复杂度为O(n)。因此该算法的时间复杂度为O(n)。