acm数论之旅--唯一分解定理

 

题目:
    给出n,问n = b^p中p符合该等式的最大值

分析:
    先求出所有n的质因子,然后对这m个质因子分类统计,比如
    n = 36时,可以分成 2个2,2个3,然后求出所有这些基数的
    最大公因数gcd。另外由于有负数的存在,所以求到的gcd若
    为偶数时,需要不断除二直到为奇数为止

根据完全P次方数的定义可知,一定会借助唯一分解定律,那么必然预处理素数表 。

但是仅仅唯一分解还是不够的,要怎么求最大的幂呢?  我们来举几个例子,加入唯一分解成2   2   3,那么答案是1 , 如果 2  2  2  ,答案是3,如果 2  2  3  3  ,答案是2, 可以发现,其实答案就是各质数的指数的最大公约数。     但是WA了,因为题目并没有说输入的一定是正整数,而且给过提示 : 输入在32位有符号整数范围内。

那么为什么负数会导致计算错误呢?  比如:-16 ,分解成4个2,但是没有一个整数可以表示出-16这个答案,因为偶次幂一定是正的。   所以,当n为负数时,答案不能为偶数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 333333;
ll n,m,vis[maxn+5]={0},prime[maxn+5],cnt=0;
void init() {
    int m = sqrt(maxn);
    for(int i=2;i<=m;i++) if(!vis[i])
        for(int j=i*i;j<=maxn;j+=i) vis[j] = 1;
    for(int i=2;i<=maxn;i++)
        if(!vis[i]) prime[cnt++] = i;
 
}
ll gcd(ll a,ll b) {
    return b == 0 ? a : gcd(b, a%b);
}
ll solve(ll n) {
    bool ok = false;
    ll ans = 0,temp = n;
    for(int i=0;i<cnt;i++) {
        ll c = 0;
        while(n % prime[i] == 0) {
            n /= prime[i];
            c++;
        }
        ans = gcd(ans,c);
        if(n == 1) break;
    }
    if(ans == 0) ans = 1;
    if(temp < 0)
        while(ans % 2 == 0) ans /= 2;
    return ans;
}
int main() {
    init();
    while(~scanf("%lld",&n)&&n) {
        ll ans = solve(n);
        printf("%lld\n",ans);
    }
    return 0;
}


n是大于一的任意正整数。(称为标准分解式)其中pi为素数,质数ai为正整数(如果ai=0,相当于乘一,没有意义的)。

标准分解式是唯一且一定存在的。(素因子的乘积顺序不考虑)

下面给出几个简单的判别式:

整数a能被2整除的充要条件是a的末尾数字为偶数。
整数a能被3整除的充要条件是a的各位数字之和能被3整除。
整数a能被5整除的充要条件是a的末尾数字为0或5 。
整数a能被11整除的充要条件是a的奇位数字之和(1.3….)和偶位数字之和(2.4…)的差的绝对值能被11整除。
(很神奇)将a写成千进制数,即a=an*1000^n + an-1 *1000^n-1 + … + a1 *1000 + a0 ,其中0<=ai<1000,则a能被7(或11或13)整除的充要条件是(a0 + a2 + …) - (a1 + a3 + …) 能被7(或11或13)整除。
暴力算法

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>

using namespace std;
const double eps = 0.001;  //用于浮点数精度控制
int Cal(int n)
{
    int i,j;
    for(i=30; i>=2; i--)
    {
        if(pow(2,i)  < n+eps)//不加会超时!
            for(j=2; j*j<=n; j++)
            {
                double t = pow(j,i);
                if(t > n-eps &&t < n+eps)
                    return i;
                if(t > n+eps)//不加会超时,下个函数一样!
                    break;
            }
    }
    return 1;

}
int Cal2(int n)
{
    int i,j;
    for(i=31; i>=3; i-=2)//偶数次方只可能是正数
    {
        if(pow(2,i) - eps < (double)(-n))
            for(j=2; j*j<=-n; j++)
            {
                double t = pow(j,i);
                if(t > -n-eps &&t < -n+eps)
                    return i;
                if(t > -n+eps)
                    break;
            }
    }
    return 1;
}
int main()
{
    int n,i;
    while(scanf("%d",&n) && n)
    {
        if(n==-2147483648)  //最小的int型数,防止越界就提出来特殊判断一下。
        {
            printf("31\n");
            continue;
        }
        if(n>0)
            printf("%d\n",Cal(n));
        else
            printf("%d\n",Cal2(n));
    }
    return 0;
}

 

 

https://blog.csdn.net/u013555159/article/details/52101898

给出一个数,求能够相乘得到他的数中,元素总和最小的一个,就是求他的质因子分解,如果只有一个质因子,那最后结果要加上1.

 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
ll counter,r;
ll init(ll n)
{
    if(n==1) return 2;
    counter=0;
    r=0;
    ll temp=(int)((double)sqrt(n)+1);
    for(ll i=2;i<=temp;i++)
    {
        if(n%i==0)
        {
            ll ans=1;
            counter++;
            while(n%i==0)
            {
                ans=i*ans;
                n=n/i;
            }
            r+=ans;
        }
    }
    if(n!=1) {r+=n;counter++;}
    if(counter==1) return r+1;
    else return r;
}
int main(int argc, const char * argv[]) {
    //freopen("/Users/zhangjiatao/Documents/暑期训练/input.txt","r",stdin);
    ll n;
    int t=0;
    while(scanf("%lld",&n)==1)
    {
        //cout<<"shit"<<endl;
        if(n==0) break;
        t++;
        cout<<"Case "<<t<<": "<<init(n)<<endl;
    }
    return 0;
}

 

posted @ 2018-10-16 20:18  downrainsun  阅读(591)  评论(0编辑  收藏  举报