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

UVaOJ-11752-The Super Powers 解题报告

2018年04月21日 ⁄ 综合 ⁄ 共 2032字 ⁄ 字号 评论关闭

       不错的思考题,可找规律。人生第一次真正意义上的写证明。题意:如果一个数是两个不同的数的幂,那么这个数就称之为超级幂,比如64 = 8^2 = 4^3。因此64是一个超级幂。没有输入,请输出0到2^64-1范围内的所有超级幂。


       我的解题思路:首先要找出超级幂的规律,根据样例明显会看出来超级幂不能算本身的1次幂,我们试着把数都分解成幂形式来看(1特殊)。

16 = 2^4 = 4^2

64 = 2^6 = 4^3 = 8^2

81 = 3^4 = 9^2

256 = 2^8 = 4^4 = 16^2

512 = 2^9 = 8^3

......

看看能找到什么规律?看一下每个数分解成幂形式的指数部分,4的因数有2,6的因数有2和3,8的因数有2和4,等等。好像是在分解质因数,那么我们可以猜出超级幂分解成指数最大的幂形式时,这个指数是一个合数。大胆假设:如果一个数可以表示成某个数的合数次幂,那么这个数是超级幂,否则不是(1特殊)。

实际上我们可以这样证明:

设X = A^a(X,A,a都是整数,a 是合数,A不为1)。

因为a是合数,所以a必能分解成两个不是a和1的数的乘积,暂且表示为a = b × c(b和c可能相等,但是不会等于1或者a本身)。

因此X = A^a = A^(b×c) = (A^b)^c,如果说我们把A^b表示成B的话,那么现在X就能够被表示成超级幂了

X = A^a = B^c 。

充分性证毕。再证必要性:

设超级幂X = A^a = B^b = C^c = ……(X,A,B,……,a,b,……都是整数,a,b,c,……等都是素数)

超级幂X至少可以表示成两种幂形式,A^a和B^b(a和b都是素数)。

那么可知A^a = B^b,可以化为A = B^(b/a)

因为a和b都是素数,所以b / a的值必定不会是整数,也就是说a不能整除b,所以嘛,呵呵,B^(b/a)就不会是整数,这与之前的条件冲突,所以嘛,超级幂必定可以表示成某个数的合数次幂。

必要性证毕。

证明完毕之后我们只需要把所有不大于2^64-1的合数次幂按顺序输出就行了。2^64-1的范围是无符号64位整型的范围,关于求幂,底数我们只需要枚举到2的16次方就行了,关于幂的合数我们只要把64范围内的数筛素数就行了。为了避免溢出,需要用到一些技巧,详情请见代码。


       我的解题代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <climits>
#include <algorithm>
#include <set>

using namespace std;

typedef unsigned long long ULong;
typedef long long Long;

const int N = 70;

bool isprime[N];
int primes[N], pn;
set<ULong> numset;

void InitRead();

void DataProcess();

void FastSieve(int maxn);

ULong FastPow(ULong base, ULong n);

int main()
{
    InitRead();
    DataProcess();
    return 0;
}

void InitRead()
{
    numset.clear();
    memset(isprime, true, sizeof(isprime));
    isprime[0] = isprime[1] = false;
    pn = 0;
    FastSieve(N - 1);
    int cnt = 1 << 16;          //底数枚举的最大值,因为最小的合数是4
    for (int i=2; i<cnt; ++i)
    {
        int temp = ceil(64 / (log(i) / log(2)));    //这个值是刚好会溢出无符号64位整型范围的指数
        for (int j=4; j<temp; ++j)
        {
            if (isprime[j]) continue;
            numset.insert(FastPow(i, j));
        }
    }
    return;
}

void DataProcess()
{
    set<ULong>::iterator si;
    puts("1");
    for (si=numset.begin(); si!=numset.end(); ++si)
    {
        printf("%llu\n", *si);
    }
    return;
}

void FastSieve(int maxn)
{
    for (int i=2; i<=maxn; ++i)
    {
        if (isprime[i]) primes[pn++] = i;
        for (int j=0; j<pn; ++j)
        {
            if (i * primes[j] > maxn) break;
            isprime[i * primes[j]] = false;
            if (i % primes[j] == 0) break;
        }
    }
    return;
}

ULong FastPow(ULong base, ULong n)
{
    ULong ans = 1;
    while (n != 0)
    {
        if (n & 1) ans *= base;
        base *= base;
        n >>= 1;
    }
    return ans;
}

抱歉!评论已关闭.