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; }