唯一分解定理的應用

一個數的唯一分解爲
n的唯一分解

1. 一個數的所有因子個數:x=(e1+1)(e2+1).......(ek+1)x=(e1+1)(e2+1).......(ek+1)

2. 一個數的所有因子和:
一個數的所有因子和

3. 用唯一分解求a,b的gcd,lcm(ak,bk爲質數的冪):
gcd(a,b)=p1min(a1,b1)p2min(a2,b2)......pkmin(ak,bk)gcd(a,b)=p_{1}^{min(a1,b1)}p_{2}^{min(a2,b2)}......p_{k}^{min(ak,bk)}
lcm(a,b)=p1max(a1,b1)p2max(a2,b2)......pkmax(ak,bk)lcm(a,b)=p_{1}^{max(a1,b1)}p_{2}^{max(a2,b2)}......p_{k}^{max(ak,bk)}

4.在不取mod的情況下,用唯一分解求組合數
C(n,m)=n!/(m!(nm)!)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)
    詳細思路別的博客有,關鍵在於這個公式的推導
    (pe+11)/(p1)(p^{e+1}-1)/(p-1)
    =(pe+1p+(p1))/(p1)=(p^{e+1}-p+(p-1))/(p-1)
    =p(pe1)/(p1)+1=p(p^{e}-1)/(p-1)+1 (等比數列公式)
    =(p+p2+p3.....+pe)+1=(p^{}+p^{2}+p^{3}.....+p^{e})+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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章