现在的位置: 首页 > 综合 > 正文

【Author : DS】{Regionals 2011, North America – Southeast USA} C题解题报告

2013年01月29日 ⁄ 综合 ⁄ 共 1999字 ⁄ 字号 评论关闭

题目地址

http://acm.bnu.edu.cn/bnuoj/external/57/5772.pdf

题目大意:

   给一个数字n (n < 75)。求最小的X , 满足 X = Y * Z (Y <= Z) 有n种方式

分析:

   此题跟我航出的Anti-Prime 反素数的题目类似, 都是直接搜索+枚举答案就好。

解法:

       0、 筛法求素数。 因为n <= 75 , 因此理论上只用求出前75个(或者更小就不细算了) 质数就行。

       1、对一般情况的判定(不包括 X = Y * Y 即平方数的问题)

               显而易见, 如果 X != Y * Y  , 那么X肯定会有 N * 2 个因数。因此,只要枚举最小的有 M (= N * 2 )个因数的数就可以。方法是搜索:

               dfs(LL now , LL step , LL cnt) 状态是 当前的乘积是 now , 已经步行到第step个素数(2 , 3 , 5 , 7 , 11 , 13.。。。), 现在一共有cnt个因数。

   对于第step个因数, 如果有k个, 即下一个状态是 now *  $ prime[step] ^{k} $ 时, 很明显因数个数增加了(k + 1) 倍,于是下一个状态就是 

dfs(now * $ prime[step] ^{k} $ , step + 1 , cnt * (k + 1) )

   剪枝:

   初始化ans = $2^{18}$ , (题目保证了答案一定小于等于这个数) , 然后对于每次转移时 , 如果 now * prime[step] > ans 的时候break

       2、对于平方数的情况, 不妨考虑这两个数:4 , 36

               4 = 1 * 4 = 2 * 2   = (分解质因数为) $2^{2}$  , 按照先前的算法应该有 2 + 1 = 3个因数,而 n = 2

               36 = 1 * 36 = 2 * 18 = 3 * 12 = 4 * 9 = 6 * 6 = (分解质因数为)  $2^{2}*3^{2}$ , 按照先前的算法应该是有 (2 + 1)* (2+ 1) = 9 个因数,而 n = 5

   为什么呢?因为对于n来说,有两个因数重合。那么对于平方数来说,只需要 n * 2 - 1 个因数就可以。因为必须是完全平方数,于是每个因数的幂就必须是偶数。只用在搜索的时候特判就好了。

   至此,这个问题就被完美的解决了。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define LL long long
const int PRIMERANGE = 300;
const LL inf = 1e18;
int prime[PRIMERANGE + 1];
int getPrime(){ // 筛法求素数, 模板
    memset(prime , 0 , sizeof(prime));
    for (int i = 2 ; i <= PRIMERANGE ;++i){
        if (!prime[i]) prime[++prime[0]] = i;
        for (int j = 1 ; j <= prime[0] && prime[j] <= PRIMERANGE / i ; j++){
            prime[prime[j] *i ] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    return prime[0];
}
LL n;
LL ans;
void dfs(LL now , LL step, LL cnt){ // 不考虑平方的情况
    if (cnt == n){
        ans = min<LL>(ans , now);   //记录当前最佳答案
        return;
    }
    LL last = n / cnt;
    for(LL i = 1 ; i <= last ; ++i){    // 枚举prime[step]的幂
        if (prime[step] * now > ans) break;     //剪枝
        now *= prime[step];
        if (last % (i + 1 )) continue;      // 剪枝, 如果 cnt * (i + 1) 不是n的约数那么放弃
        dfs(now , step + 1 , cnt* (i + 1)); //  状态转移
    }
}
void dfssquare(LL now , LL step, LL cnt){   // 特判完全平方数
    if (cnt == n){
        ans = min<LL>(ans , now);
        return;
    }
    LL last = n / cnt;
    for(LL i = 2 ; i <= last ; i += 2){     // 幂必须为偶数
        if (now * prime[step] > ans) break;
        now *= prime[step] * prime[step];
        //if (now > ans) break;
        if (last % (i + 1 )) continue;
        dfssquare(now , step + 1 , cnt* (i + 1));
    }
}
void solve(){
    n*= 2;          // 忽略完全平方数的情况
    ans = inf;
    dfs(1 , 1 , 1);
    LL other = ans;
    ans = inf;

    n /= 2;         // 特判完全平方数
    n = 2 * n - 1;
    dfssquare(1 , 1 , 1);


    ans = min<LL>(ans , other); //  答案取小
    printf("%I64d\n" , ans);
}
int main(){
    getPrime();
    while(cin >> n , n) solve();
}

抱歉!评论已关闭.