ACM - ICPC World Finals 2013 D Factors

原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf

题目翻译:

问题描述

  一个最基本的算数法则就是大于1的整数都能用1个或多个素数相乘的形式表示出来。当然,可以安排出多种的质因子排列方案,例如:10=2*5=5*2 20=5*2*2=2*5*2=2*2*5
  让我们用f(k)表示k的质因子排列方案数,如f(10)=2,f(20)=3。
  给你一个正整数n,至少有一个k使得f(k)=n,我们想知道最小的k是多少。

输入格式

  输入文件至多有1000组数据,每组数据单独成行上,包含一个正整数n(n<2^63)。

输出格式

  对于每组数据,输出他的问题n和最小的满足f(k)=n的k(k>1),数据保证k<2^63。

样例输入

1
2
3
105

样例输出

1 2
2 6
3 12
105 720

题目大意:假如一个大于2的正整数n的所有质因子的排列方式数为p(相同质因子调换位置不算),求满足条件的最小的n。

思路分析:

这一看又是一道数学题(而且是一道相当暴力的数学题……),首先我们将问题反过来,假如给出一个数n,求它的所有质因子排列方式数p。这个问题不难解决,我们先对n进行质因子分解,得到以下这个式子(其中\(p_i\)代表第i个质数)\[n=2^{a_1}\cdot 3^{a_2}\cdot 5^{a_3}\cdots p_{i}^{a_i},\]然后我们很轻易地通过组合公式得到如下这个答案\[p=\frac{\left(a_1+a_2+a_3+\cdots+a_i\right)!}{a_1!\cdot a_2!\cdot a_3!\cdots a_i!} \qquad (*) ,\]

这样我们就有了思路:将p转换成(*)式的形式,然后求出对应的序列\(\{a_i\}\),然后将第i个质数的指数赋为\(a_i\),然后反着求出n就好了。说起来容易但是算起来费劲……那么多数字的阶乘根本无法计算,我们要想办法缩小问题的规模,设\(\{a_i\}\)的位数为m,设\(w=\sum_{k=1}^m a_k\),考虑到题目中限定的n和p都小于\(2^{63}-1\),而根据我们的定义一定有\(n\le2^w\),而且由于\(a_i>0且a_i\ge a_{i+1}\),所以必然有\(m<63,\quad w<63\),这样我们就将问题的规模限定了,但是63!还是很难直接计算,所以我们需要对(*)式进行等价变换,将它限制在63以内的组合数的运算中,我们可以得到\[\frac{\left(a_1+a_2+a_3+\cdots+a_i\right)!}{a_1!\cdot a_2!\cdot a_3!\cdots a_i!}={w \choose a_1}\cdot{w-a_1 \choose a_2}\cdot{w-a_1-a_2 \choose a_3}\cdots{a_m \choose a_m}\qquad(**)\]

至此我们又看到了一丝希望,我们可以通过枚举所有计算出来的(**)式值在\(2^{63}\)以内的序列\(\{a_i\}\)然后计算它们对应的(**)式值和n,然后按照(**)式值进行排序,然后根据给出的询问在暴搜得到的序列中二分出来答案即可

(不知不觉这道题就讲完了)

算法流程:

1.打表前63个质数(这个手动打表就可以)

2.递推计算出所有63以内的组合数的值,备用

3.暴搜序列\(a_i\),同时计算出它们对应的p和n(一道算到某一步时p或n有爆long long的嫌疑立刻抛弃之,不符合数据范围)

4.以p为关键字排序

5.读入,二分出结果

复杂度分析:

这是一个暴搜,搜出来的序列数不会超过50000个,然后……就没有然后了(这个复杂度太难估算)

参考代码:

  1 //date 20130122
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 
  6 typedef long long LL;
  7 
  8 const LL MAX = 0x7FFFFFFFFFFFFFFFLL;
  9 
 10 int c[100][100];
 11 const int prime[100] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523};
 12 LL x;
 13 
 14 int stat[50000][40];
 15 int a[100];
 16 int count;
 17 int p = 1;
 18 
 19 long long dp[70][70];
 20 
 21 void work(int n, int m)
 22 {
 23     if(n < m){dp[n][m] = 0; return;}
 24     if(n == m || m == 0){dp[n][m] = 1; return;}
 25     if(m == 1){dp[n][m] = n; return;}
 26     
 27     if(dp[n - 1][m] == -1)work(n - 1, m);
 28     if(dp[n - 1][m - 1] == -1)work(n - 1, m - 1);
 29     dp[n][m] = dp[n - 1][m - 1] + dp[n - 1][m];
 30 }
 31 
 32 
 33 struct pair
 34 {
 35     long long n, k;
 36 }num[50000];
 37 
 38 inline bool cmp(pair A, pair B)
 39 {
 40     if(A.n != B.n)return A.n < B.n;
 41     else return A.k < B.k;
 42 }
 43 
 44 void DFS(int k, int last, int sum)
 45 {
 46     if(sum > 63)return;
 47     for(int i = 1; (i <= last) && (sum + i <= 63); ++i)
 48     {
 49         a[k] = i;
 50         long long now = 1;
 51         int sign = 0;
 52         int cur = sum + a[k]; long long q = 1;
 53         for(int j = 1; j <= k; ++j) 
 54         {
 55             if(MAX / dp[cur][a[j]] < q){return;}
 56             else{q *= dp[cur][a[j]]; cur -= a[j];}
 57             for(int w = 1; w <= a[j]; ++w)
 58             {
 59                 if(MAX / prime[j] < now)return;
 60                 now *= prime[j];
 61             }
 62         }
 63         for(int j = 1; j <= k; ++j)
 64         {
 65             stat[count][j] = a[j];
 66         }
 67         ++count;
 68         num[count].n = q; num[count].k = now;
 69         DFS(k + 1, i, sum + i);
 70     }
 71 }
 72 
 73 inline void hittable()
 74 {
 75     count = 1;
 76     memset(dp, 0xFF, sizeof dp);
 77     dp[1][1] = 1;
 78     for(int i = 1; i <= 63; ++i)dp[i][0] = 1;
 79     for(int i = 1; i <= 63; ++i)
 80         work(63, i);
 81     DFS(1, 64, 0);
 82 }
 83 
 84 inline LL solve(LL x)
 85 {
 86     int l = 2, r = p, mid;
 87     while(l < r)
 88     {
 89         mid = (l + r) >> 1;
 90 //        printf("%d %d %d %lld %lld\n", l, r, mid, num[mid].n);
 91         if(num[mid].n == x)return num[mid].k;
 92         if(num[mid].n < x)l = mid + 1;
 93         else r = mid - 1;
 94     }
 95     return num[l].k;
 96 }
 97 
 98 int main()
 99 {
100     freopen("factors.in", "r", stdin);
101     freopen("factors.out", "w", stdout);
102     
103     hittable();
104     std::sort(num + 1, num + count + 1, cmp);
105     long long w = num[1].n;
106     for(int i = 2; i <= count; ++i)
107     {
108         while(num[i].n == w){num[i].n = MAX; ++i;}
109         ++p; w = num[i].n;
110     }
111     std::sort(num + 1, num + count + 1, cmp);
112     
113     while(scanf("%I64d", &x) != EOF)
114     {
115         LL w = solve(x);
116         printf("%I64d %I64d\n", x, w);
117     }
118     return 0;
119 }

需要注意的问题:

1.打表要用机器打表,否则代码长度会太大……

2.判断是否会爆的时候不能直接比较(那样就已经乘爆了),应该把它转化成除法比较

posted on 2014-01-27 15:32  SnowyJone  阅读(1228)  评论(0编辑  收藏  举报