唯一分解定理
1.定理介绍:
任何大于1的整数都可以表示成质因子的幂次相乘的形式,即
X
=
p
1
k
1
p
2
k
2
p
3
k
3
…
…
p
n
k
n
X=p_1^{k_1}p_2^{k_2}p_3^{k_3}……p_n^{k_n}
X=p1k1p2k2p3k3……pnkn
习惯上把质因数小的写前面
例如:
12
=
2
2
×
3
,
120
=
2
3
×
3
×
5
12=2^2×3,120=2^3×3×5
12=22×3,120=23×3×5
2.定理的几大应用
(一)因数个数和因数求和
根据组合数学的知识容易得出,每个质因子在因数中出现的个数是 [ 0 , k i ] [0,k_i] [0,ki],(0就表示没出现),所以因数总个数 S u m = ∏ i = 1 n ( k i + 1 ) = ( k 1 + 1 ) ( k 2 + 1 ) … … ( k n + 1 ) Sum=\prod_{i=1}^n(k_i+1)=(k_1+1)(k_2+1)……(k_n+1) Sum=∏i=1n(ki+1)=(k1+1)(k2+1)……(kn+1)
同理可得所有因数的和的公式: S u m = ∏ i = 1 n ( ∑ j = 0 k j p i j ) = ( 1 + p 1 + p 1 2 + … … p 1 k 1 ) ( 1 + p 2 + p 2 2 + … … p 2 k 2 ) … … ( 1 + p n + p n 2 + … … p n k n ) Sum=\prod_{i=1}^n(\sum_{j=0}^{k_j}p_i^j)=(1+p_1+p_1^2+……p_1^{k_1})(1+p_2+p_2^2+……p_2^{k_2})……(1+p_n+p_n^2+……p_n^{k_n}) Sum=∏i=1n(∑j=0kjpij)=(1+p1+p12+……p1k1)(1+p2+p22+……p2k2)……(1+pn+pn2+……pnkn)
例题:LightOJ 1341
题意:给出T组a,b,求满足条件长方形(x,y)的个数,xy=a,且b<=x<y
做法:先用线性筛预处理所有的质数,对a进行唯一分解,根据乘法公式计算因数个数,由于题目说了不可能是正方形,所以直接对因数个数/2,再暴力减去小于b的因数个数
小声bb:如果数据是4000组1e12,5e5,那么每次暴力求小于b的因数个数时间复杂度 O ( T b ) O(Tb) O(Tb)不就是1e9级别的吗。。。是数据太水了吗?
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=1e6+7;
const int INF=1e9;
const ll INFF=1e18;
int prime[maxn];
int primenum[maxn];
int m;
void prime_init()//质数线性筛
{
m=0;
rep(i,2,maxn)prime[i]=1;
prime[0]=prime[1]=0;
repp(i,2,maxn)
{
if (prime[i])
{
primenum[m++]=i;
}
for (int j=0;j<m&&i*primenum[j]<maxn;j++)
{
prime[i*primenum[j]]=0;
if (i%primenum[j]==0)break;
}
}
}
int solve(ll x)//分解求因子数量
{
int res=1;
for (int i=0;i<m&&primenum[i]<=sqrt(x);i++)
{
int num=0;
while(x%primenum[i]==0)
{
x/=primenum[i];
num++;
}
res*=(num+1);//跟公式对比
}
if (x>1)res*=2;//说明还可以整除一个大质数,幂次肯定是1,所以×2,例如28=2^2*7
return res;
}
int main()
{
int t;
ll a,b;
scanf("%d",&t);
prime_init();
rep(K,1,t)
{
scanf("%lld%lld",&a,&b);
if (b*b>a)printf("Case %d: %d\n",K,0);//说明不存在最小边长方形
else
{
int ans=solve(a);
ans/=2;
repp(i,1,b)
{
if (a%i==0)ans--;
}
printf("Case %d: %d\n",K,ans);
}
}
return 0;
}
例题2
LightOJ1336
题意:给出T个n,求1-n中有几个数字的因数之和是偶数
做法:根据因数求和公式可得
S
u
m
=
∏
i
=
1
n
(
∑
j
=
0
k
j
p
i
j
)
=
(
1
+
p
1
+
p
1
2
+
…
…
p
1
k
1
)
(
1
+
p
2
+
p
2
2
+
…
…
p
2
k
2
)
…
…
(
1
+
p
n
+
p
n
2
+
…
…
p
n
k
n
)
Sum=\prod_{i=1}^n(\sum_{j=0}^{k_j}p_i^j)=(1+p_1+p_1^2+……p_1^{k_1})(1+p_2+p_2^2+……p_2^{k_2})……(1+p_n+p_n^2+……p_n^{k_n})
Sum=∏i=1n(∑j=0kjpij)=(1+p1+p12+……p1k1)(1+p2+p22+……p2k2)……(1+pn+pn2+……pnkn)
那么显然如果这n项多项式中有一项是偶数的话,最后的因子和肯定是偶数,所以我们选择求奇数的个数,然后用n减去它得到最后答案
1.
当2作为质因子的时候,由于 2 , 2 2 , 2 3 2,2^2,2^3 2,22,23都是偶数,所以 ( 1 + 2 + 2 2 + … … 2 k 1 ) (1+2+2^2+……2^{k_1}) (1+2+22+……2k1)肯定是奇数
2.
除去2的其他质数肯定都是奇数,所以当 k i k_i ki是偶数的时候, p i , p i 2 , p i n p_i,p_i^2,p_i^n pi,pi2,pin肯定都是奇数, ( 1 + p i + p i 2 + … … p i k i ) (1+p_i+p_i^2+……p_i^{k_i}) (1+pi+pi2+……piki)肯定是奇数,因为多了个1在前面。当 k i k_i ki全是偶数的时候,才可以满足最后乘积是奇数,也就是说该数字是个完全平方数
最终就是
如果这个数是完全平方数,肯定符合因数和是奇数的条件的
还有一个就是如果这个数字包含2这个质因子,并且2的幂次是奇数次,所以导致这个数字不一定是完全平方数,解决办法就是除2,将2的幂次转化为偶数
所以最终答案就是 n − n − n 2 n-\sqrt{n}-\sqrt{n\over 2} n−n−2n
/**
* Author1: low-equipped w_udixixi
* Author2: Sher丶lock
* Date :2019-09-19
**/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e6+7;
const int INF=1e9;
const ll INFF=1e18;
int main()
{
int t;
ll n;
scanf("%d",&t);
rep(K,1,t)
{
scanf("%lld",&n);
printf("Case %d: %lld\n",K,n-(ll)sqrt(n)-(ll)sqrt(n/2));
}
return 0;
}