质数、约数

分解质因数(区分质因子和约数!!)

void divide(int x){
    for(int i=2;i<=x/i&&x>1;i++){
    	if(x%i==0){
    		int s=0;
    		while(x%i==0){
    			s++;
    			x/=i;
    		}
    		cout<<i<<" "<<s<<endl;
    	}
    }
    if(x>1) cout<<x<<" "<<1<<endl;
    cout<<endl;
}

分解N!的质因数 

197. 阶乘分解 - AcWing题库

        多个数相乘的质因子次数,等于将每个数的质因子次数相加

        先筛质数,然后计算每个质数在1~N出现的次数之和

#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n;
int prime[N],cnt;
bool vis[N];
int res[N];
void get_prime(int n){
    for(int i=2;i<=n;i++){
    	if(!vis[i]){
    		prime[++cnt]=i;
    		vis[i]=true;
    	}
    	for(int j=1;prime[j]*i<=n;j++){
    		vis[prime[j]*i]=true;
    		if(i%prime[j]==0) break;
    	}
    }
}
int main(){
    cin>>n;
    get_prime(n);
    for(int i=1;i<=cnt;i++){
    	int p=prime[i],s=0;
    	for(int j=n;j;j/=p){
    		s+=j/p;
    	}
    	res[p]+=s;
    }
    for(int i=1;i<=cnt;i++){
    	int p=prime[i];
		if(res[p]!=0){
			cout<<p<<" "<<res[p]<<endl;
		}
    }
}

 筛质数

埃式筛 O(nloglogn)

线性筛 O(n)

int prime[N];
bool vis[N];
int cnt;
//埃式筛 
void get_prime1(int n){
	for(int i=2;i<=n;i++){
		if(vis[i]) continue;
		prime[cnt++]=i;
		for(int j=2;j*i<=n;j++){
			vis[i*j]=true;
		}	
	}
}
//线性筛
void get_prime2(int n){
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			prime[cnt++]=i;
		}	
        //注意,不管是不是质数都要执行这里
		for(int j=0;prime[j]*i<=n;j++){
			int t=prime[j]*i;
			vis[t]=true;
			if(i%prime[j]==0) break;
		}
	}
} 

质数距离(筛大区间[l,r]的质数)

 196. 质数距离 - AcWing

        1≤L<U≤2^31−1 先筛出小区间的质数,对于一个合数x,必然存在小于sqrt(x)的质因子,所以线性筛筛出[1,50000]的质数(50000 = sqrt(2^31)) 

        对于区间[L,U],使用埃筛的思想,对于每个质数p,筛掉区间内p的倍数,最终得到的即为[L,U]内的质数 (注意将[L,U]映射到[0,U-L])

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=5e4+5,M=1e6+5;
int prime[N];
int pcnt;
bool vis[N];
void init(){//筛出1~50000的质数 (50000 = sqrt(2^31)) 
	for(int i=2;i<N;i++){
		if(!vis[i]){
			prime[++pcnt]=i;
		}
		for(int j=1;i*prime[j]<N;j++){
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) break;
		}
	}
}
int st[M];
int pprime[M];
int cnt;
//对于一个合数x,必然存在小于sqrt(x)的质因子 
int main(){
	init();
	int l,r;
	while(cin>>l>>r){
		memset(st,false,sizeof st);
		for(int i=1;i<=pcnt;i++){
			ll p=prime[i];
			//埃筛 p是质数 筛掉[l,r]内p的倍数 
			//(l+p-1)/p 大于L且离L最近的P的倍数
			for(ll j=max(2*p,(l+p-1)/p*p);j<=r;j+=p){
				//[l,r]的第j-l个被筛掉 
				st[j-l]=true; 
			}
		}
		//[l,r]的质数 
		cnt=0;
		for(int i=0;i<=r-l;i++){
			if(!st[i]&&i+l>=2){
				pprime[++cnt]=i+l;
			}
		}
		//枚举相邻的两个质数 
		if(cnt<2){
			printf("There are no adjacent primes.\n");
		}else{
			int minpos=0,maxpos=0;
			int minn=0x3f3f3f3f,maxn=-1;
			for(int i=1;i+1<=cnt;i++){
				int d=pprime[i+1]-pprime[i];
				if(d<minn){
					minn=d;
					minpos=i;
				}
				if(d>maxn){
					maxn=d;
					maxpos=i;
				}
			}
			printf("%d,%d are closest, %d,%d are most distant.\n",pprime[minpos],pprime[minpos+1],pprime[maxpos],pprime[maxpos+1]);
		}
	}
}

求约数

vector<int> get_divisors(int x)
{
    vector<int> res;
    //从i开始,不改变x
    for(int i=1;i<=x/i;i++){
        if(x%i==0){
    		res.push_back(i);
    		if(i!=x/i) res.push_back(x/i);
    	}
    }
    sort(res.begin(), res.end());
    return res;
}

约数个数 约数之和

ll res;
map<int,int> mp;
//如果分解完质因数后,N = p1^c1 * p2^c2 * ... *pk^ck
//约数个数等于   (c1 + 1) * (c2 + 1) * ... * (ck + 1)
//约数之和等于  (p1^0 + p1^1 + ... + p1^c1) * ... * (pk^0 + pk^1 + ... + pk^ck) 
void divide(int x){
	for(int i=2;i<=x/i;i++){
		if(x%i==0){
			int s=0;
			while(x%i==0){
				s++;
				x/=i;
			}
			mp[i]+=s;
		}
	}
	if(x>1) mp[x]++;
}
//约数个数 
ll solve1(){
	ll res=1;
	for(auto t:mp){
		res*=(t.second+1);
		res%=mod;
	}
	return res;
}
//约数之和
ll solve2(){
	ll res=1;
	for(auto t:mp){
		int p=t.first;
		int c=t.second;
		ll s=1;
		while(c--)//算 p1^0 + p1^1 + ... + p1^c1的技巧 
			s=(s*p+1)%mod;
		res*=s%mod;
		res%=mod;
	}
	return res;
}
int main(){
	int n;cin>>n;
    //求x=a0*a1*a2*....的约数个数/约数之和
    //先分解x质因数
	for(int i=1;i<=n;i++){
		cin>>a[i];
		divide(a[i]);
	}
    //约数个数
	cout<<solve1()<<endl;
    //约数之和
    cout<<solve2()<<endl;
}

1~N各自的约数之和

for(int i=1;i<=n;i++) //约数i
    for(int j=2;i*j<=n;j++) //i*j的约数包含i
        sum[i*j]+=i;

题记 

轻拍牛头

1291. 轻拍牛头 - AcWing

TLE代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5,M=1e6+5;
int f[M],w[N];
int cnt[M];
int n; 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
		cnt[w[i]]++;
	}
	for(int i=1;i<=n;i++){
		for(int j=2;w[i]*j<M;j++){
			f[w[i]*j]++;
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",f[w[i]]+cnt[w[i]]-1);
	}

} 

        由于数有很多重复的数,而重复的数答案是相同的,所以只需要计算一次,枚举数的范围,对于一个数i,为i*j都加上i出现的次数,相当于同时计算了重复的i,最后算答案时去掉自己即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5,M=1e6+5;
int f[M],w[N];
int cnt[M];
int n; 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
		cnt[w[i]]++;
	}
	//一样的数只计算一次 
	for(int i=1;i<M;i++){
		if(!cnt[i]) continue;
		for(int j=1;i*j<M;j++){
			f[i*j]+=cnt[i];
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d\n",f[w[i]]-1);
	}

} 

樱花

1294. 樱花 - AcWing

反素数 

198. 反素数 - AcWing题库

        求不超过N的范围中,约数最多的数的最小值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+5;
int prime[]={2,3,5,7,11,13,17,19,23};
int res,maxn;
int n;
//第u个质因子 上个质因子幂数last  当前数num  约数个数sum 
void dfs(int u,int last,int num,int sum){
	if(sum>maxn||(sum==maxn&&num<res)){
		maxn=sum;
		res=num;
	} 
	if(u==9) return;
	for(int i=1;i<=last;i++){
		if((ll)num*prime[u]>n) return;
		num*=prime[u];
		dfs(u+1,i,num,sum*(i+1));
	}
}
int main(){
	cin>>n;
	dfs(0,30,1,1);
	cout<<res<<endl;
} 

Hankson的趣味题

200. Hankson的趣味题 - AcWing

        先筛质数,用质数分解b的质因子,对所有质因子DFS得到b的所有约数,逐个判断

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=5e4+5;
int prime[N];
int pcnt;
bool vis[N];
void init(){
	for(int i=2;i<N;i++){
		if(!vis[i]){
			prime[++pcnt]=i;
		}
		for(int j=1;i*prime[j]<N;j++){
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) break;
		}
	}
}
int res;
int x[N];
int xcnt;
PII f[N];
int fcnt;
int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
//枚举质因子和次幂  相乘得到所有约数 
void dfs(int u,int p){
	if(u==fcnt+1){
		x[++xcnt]=p;
		return;
	}
	for(int i=0;i<=f[u].second;i++){
		dfs(u+1,p);
		p*=f[u].first;
	}
}
int main(){
	int t;cin>>t;
	init();
	while(t--){
		int a,b,c,d;cin>>a>>b>>c>>d;
		int t=d;
		fcnt=0;
		for(int i=1;prime[i]<=t/prime[i];i++){
			if(t%prime[i]==0){
				int s=0;
				while(t%prime[i]==0){
					t/=prime[i];
					s++;
				}
				f[++fcnt]={prime[i],s};
			}
		}
		if(t>1) f[++fcnt]={t,1};
		xcnt=0;
		dfs(1,1);
		res=0;
		for(int i=1;i<=xcnt;i++){
			if(gcd(x[i],a)==b&&(ll)x[i]*c/gcd(x[i],c)==d){
				res++;
			}
		}
		cout<<res<<endl;
	}
}

约数

4874. 约数 - AcWing题库

        判断一个数的约数个数是否为3(不能暴力)

        ① 结论:如果一个数的约数个数是奇数,那么它一定是一个平方数,即sqrt(x)*sqrt(x)==x

        ②

         如果约数个数为3,最多只能有一个质因子(即约数除了1和x,只剩一个质因子),则a+1=3 ,即 a=2,也就是说x是某个质数的平方,判断sqrt(x)是否为质数即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5,M=1e6+5;
int prime[M];
int cnt;
bool vis[M];
void init(){
	for(int i=2;i<M;i++){
		if(!vis[i]){
			prime[++cnt]=i;
		}
		for(ll j=1;prime[j]*i<M;j++){
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) break;
		}
	}
}
bool judge(ll x){
	//1特判
	if(x==1) return false; 
	//如果一个数的约数个数是奇数,那么它一定是一个平方数
	ll t=sqrt(x);
	if(t*t!=x) return false;
	if(vis[t]) return false;
	return true;
}
int main(){
	init();
	int n;scanf("%d",&n);
	for(int i=1;i<=n;i++){
		ll x;
		scanf("%lld",&x);
		if(judge(x)){
			printf("YES\n");
		}else{
			printf("NO\n");
		}
	}
} 

约数之和

97. 约数之和 - AcWing

A的约数之和为(1+p^1+p^2+....+p^k)*......
A^B的约数之和为(1+p^(1*B)+p^(2*B)+....+p^(k*B))*......

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int N=5e7+5,mod=9901;
int a,b;
ll res=1;
int qmi(int a,int k){
	int res=1;
	while(k){
		if(k&1) res=(ll)res*a%mod;
		k>>=1;
		a=(ll)a*a%mod;
	}
	return res;
}
//1+p^1+p^2+....+p^k
int sum(int p,int k){
	if(k==0) return 1;
	if(k&1){
		return (1+qmi(p,k/2+1))*sum(p,k/2)%mod;
	}else{
		return (p%mod*sum(p,k-1)+1)%mod;
	}
}
int main(){
	scanf("%d%d",&a,&b);
	for(int i=2;i<=a/i;i++){
		if(a%i==0){
			int s=0;
			while(a%i==0){
				a/=i;
				s++;
			}
			//A的约数之和为(1+p^1+p^2+....+p^k)*(1+p^1+p^2+....+p^k)...
			//A^B的约数之和为(1+p^(1*B)+p^(2*B)+....+p^(k*B))*(1+p^(1*B)+p^(2*B)+....+p^(k*B))...
			res=(ll)res*sum(i,s*b)%mod;
		}
	}
	if(a==0){
		cout<<0;
		return 0;
	}
	if(a!=1){
		res=(ll)res*sum(a,b)%mod;
	}
	cout<<res;
		
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vic.GoodLuck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值