zoukankan      html  css  js  c++  java
  • ACM

    原题下载: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 ight)!}{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),而根据我们的定义一定有(nle2^w),而且由于(a_i>0且a_ige a_{i+1}),所以必然有(m<63,quad w<63),这样我们就将问题的规模限定了,但是63!还是很难直接计算,所以我们需要对(*)式进行等价变换,将它限制在63以内的组合数的运算中,我们可以得到[frac{left(a_1+a_2+a_3+cdots+a_i ight)!}{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
    ", 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
    ", x, w);
    117     }
    118     return 0;
    119 }

    需要注意的问题:

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

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

  • 相关阅读:
    【转】 cin、cin.get()、cin.getline()、getline()、gets()等函数的用法
    HDU How many prime numbers
    《大学ACM的总结 》(转载)
    POJ 最小公倍数
    HDU 开门人和关门人
    HDU shǎ崽 OrOrOrOrz
    HDU Saving HDU 2111
    HDU 1106 排序
    strtok函数()
    HDU 2187汶川地震
  • 原文地址:https://www.cnblogs.com/w007878/p/3534893.html
Copyright © 2011-2022 走看看