一个数的唯一分解为
1. 一个数的所有因子个数: x = ( e 1 + 1 ) ( e 2 + 1 ) . . . . . . . ( e k + 1 ) x=(e1+1)(e2+1).......(ek+1) x=(e1+1)(e2+1).......(ek+1)
2. 一个数的所有因子和:
3. 用唯一分解求a,b的gcd,lcm(ak,bk为质数的幂):
g
c
d
(
a
,
b
)
=
p
1
m
i
n
(
a
1
,
b
1
)
p
2
m
i
n
(
a
2
,
b
2
)
.
.
.
.
.
.
p
k
m
i
n
(
a
k
,
b
k
)
gcd(a,b)=p_{1}^{min(a1,b1)}p_{2}^{min(a2,b2)}......p_{k}^{min(ak,bk)}
gcd(a,b)=p1min(a1,b1)p2min(a2,b2)......pkmin(ak,bk)
l
c
m
(
a
,
b
)
=
p
1
m
a
x
(
a
1
,
b
1
)
p
2
m
a
x
(
a
2
,
b
2
)
.
.
.
.
.
.
p
k
m
a
x
(
a
k
,
b
k
)
lcm(a,b)=p_{1}^{max(a1,b1)}p_{2}^{max(a2,b2)}......p_{k}^{max(ak,bk)}
lcm(a,b)=p1max(a1,b1)p2max(a2,b2)......pkmax(ak,bk)
4.在不取mod的情况下,用唯一分解求组合数
C
(
n
,
m
)
=
n
!
/
(
m
!
∗
(
n
−
m
)
!
)
C(n,m)=n!/(m!*(n-m)!)
C(n,m)=n!/(m!∗(n−m)!)
用唯一分解求出分母和分子的唯一分解,然后约分,约掉素因子就不会爆long long范围。
拓展:如果给题目给你一个素数mod,就可以用阶乘逆元求.。
素数筛:
const int N=1e6+5;
int p[N],k=0;
bool vis[N];
void prime(int n){
memset(vis,0,sizeof(vis));
int m=(int)sqrt(n+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<=n;j+=i)vis[j]=1;//写j=i*i,,比2*i更快,而且效果相同
for(int i=2;i<=n;i++)if(!vis[i])p[k++]=i;
}
- 求一个数的所有因子个数(LightOJ1341)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int p[N],k=0;
bool vis[N];
void prime(int n){
memset(vis,0,sizeof(vis));
int m=(int)sqrt(n+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<=n;j+=i)vis[j]=1;
for(int i=2;i<=n;i++)if(!vis[i])p[k++]=i;
}
ll cal(ll x){//算x的因子个数
ll cnt=1,t;
for(ll i=0;p[i]*p[i]<=x&&i<k;i++){//p[i]*p[i]<=x比p[i]<=x省下更多时间
if(x%p[i]==0){
t=0;
while(x%p[i]==0){x/=p[i],t++;}
cnt*=(t+1);
}
}
if(x>1)cnt*=2;
return cnt;
}
int main(){
prime(N);
int t,kas=0;
scanf("%d",&t);
ll a,b;
while(t--){
scanf("%lld%lld",&a,&b);
if(b*b>=a)printf("Case %d: 0\n",++kas);//写等于号因为题目说不是正方形
else {
ll ans=cal(a)/2;
for(int i=1;i<b;i++)if(a%i==0)ans--;
printf("Case %d: %lld\n",++kas,ans);
}
}
}
- 求一个数所有因子的和
1.(LightOJ1336)
详细思路别的博客有,关键在于这个公式的推导
( p e + 1 − 1 ) / ( p − 1 ) (p^{e+1}-1)/(p-1) (pe+1−1)/(p−1)
= ( p e + 1 − p + ( p − 1 ) ) / ( p − 1 ) =(p^{e+1}-p+(p-1))/(p-1) =(pe+1−p+(p−1))/(p−1)
= p ( p e − 1 ) / ( p − 1 ) + 1 =p(p^{e}-1)/(p-1)+1 =p(pe−1)/(p−1)+1 (等比数列公式)
= ( p + p 2 + p 3 . . . . . + p e ) + 1 =(p^{}+p^{2}+p^{3}.....+p^{e})+1 =(p+p2+p3.....+pe)+1
因为p是奇数,由上可知,当e为偶数时,原式为奇数。反之相反;
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 1000005
using namespace std;
typedef long long ll;
int main(){
int t,k=0;
ll a;
scanf("%d",&t);
while(t--){
scanf("%lld",&a);
printf("Case %d: %lld\n",++k,a-(ll)sqrt(a)-(ll)sqrt(a/2));
//减去范围内平方数的个数和平方数的2倍的个数
}
}
2.(ZOJ4040)
题目题解
- 用唯一分解求lcm(LightOJ1236)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=1e7+100;
int p[N/10],k=0;
bool vis[N];
void prime(){
memset(vis,0,sizeof(vis));
ll m=(ll)sqrt(N+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<N;j+=i)vis[j]=1;
for(int i=2;i<N;i++)if(!vis[i])p[k++]=i;
}
ll cal(ll x){
ll ret=1;
for(int i=0;p[i]*p[i]<=x&&i<k;i++){
if(x%p[i]==0){
ll t=0;
while(x%p[i]==0){t++,x/=p[i];}
ret*=2*t+1;
}
}
if(x>1)ret*=3;
ret/=2,ret++;
return ret;
}
int main(){
prime();
int t,kas=1;
ll n;
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
printf("Case %d: %lld\n",kas++,cal(n));
}
}
求组合数
1.没有mod,用唯一分解求(推荐练习UVA10375)
//求C(n,m),这不是题解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+100;
bool vis[N];
int p[N/10],tot=0,a[N/10];
void prime(int n){
memset(vis,0,sizeof(vis));
int m=(int)sqrt(n+0.5);
for(int i=2;i<=m;i++)if(!vis[i])
for(int j=i*i;j<n;j+=i)vis[j]=1;
for(int i=2;i<n;i++)if(!vis[i])p[tot++]=i;
}
void cal(int n,int flag){//计算阶乘的唯一分解
for(int i=0;i<tot&&p[i]<=n;i++){
int temp=n,sum=0;
while(temp){
sum+=temp/p[i];
temp/=p[i];
}
a[i]+=sum*flag;
}
}
int main(){
int n,m;
prime(N);
while(~scanf("%d%d",&n,&m)){
ll ans=1;
memset(a,0,sizeof(int)*(n+10));
cal(n,1);cal(m,-1);cal(n-m,-1);
for(int i=0;i<tot&&p[i]<=n;i++)
ans*=(ll)pow(p[i],a[i]);//这里也可以用快速幂加速
printf("%lld\n",ans);
}
}
2.有素数mod,用阶乘逆元求
#include<cstdio>
typedef long long LL;
const LL N=1e5+10,mod = 1e9 + 7; //计算组合数取mod(mod为质数)
LL fac[N],inv[N];
LL fp(LL a,LL b)
{
LL ans = 1;
while (b){
if (b&1)ans = ans * a % mod;
a = a*a % mod;
b >>= 1;
}
return ans;
}
void init()
{
fac[0] = inv[0] = 1;
for (int i = 1;i<= N+100;i++)fac[i]=fac[i-1]*i%mod;
inv[N]=fp(fac[N],mod-2);
for(int i=N;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
int main()
{
init();
int n,m;
while (~scanf ("%d %d",&n,&m))
printf ("%lld\n",fac[n] * inv[n-m] % mod * inv[m] % mod);
}