一個數的唯一分解爲
1. 一個數的所有因子個數:
2. 一個數的所有因子和:
3. 用唯一分解求a,b的gcd,lcm(ak,bk爲質數的冪):
4.在不取mod的情況下,用唯一分解求組合數
用唯一分解求出分母和分子的唯一分解,然後約分,約掉素因子就不會爆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爲偶數時,原式爲奇數。反之相反;
#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);
}