[kuangbin]数学训练四 数论 [Cloned]

这是一篇关于数论的训练,涵盖了欧拉函数、分解因子、唯一分解定理等多个数论概念的应用,包括求欧拉函数的平方和、分解因子的方法、计算LCM、求解因数个数等问题,通过实例解析并提供了相应的解题思路和算法实现。
摘要由CSDN通过智能技术生成
A - Mathematically Hard——欧拉函数的简单应用

题意
求1~n的欧拉函数的平方的和。
思路
打表求出5e6内的欧拉函数,然后再求平方的前缀和。
需要注意的两点:
1.要用unsigned long long ,只用long long的话范围不够。

2.注意内存,开两个数组似乎就会爆空间。

先用了线性筛法,一直疯狂爆内存,换了埃式筛法,就过了 …我好南啊。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll unsigned long long
using namespace std;

const int N = 5e6 + 10;
ll phi[N] = {
   0};
int cas,a,b;

void euler1(int n) {
   
	for(int i = 2; i < n; i++) phi[i] = i;
	for(int i = 2; i < n; i++) {
   
		if(phi[i] == i) {
   
			for(int j = i; j < n; j += i) {
   
				phi[j] = phi[j] / i * (i - 1);
			}
		}
	}
    for(int i = 2 ; i < n; i++){
   
        phi[i] = (ll)(phi[i-1] + phi[i] * phi[i]);
    }
}
int main(){
   
    euler1(N-5);
    scanf("%d",&cas);
    int cnt = 0;
    while(cas--){
   
        scanf("%d %d",&a,&b);
        printf("Case %d: %llu\n",++cnt,phi[b] - phi[a-1]);
    }
    return 0;
}
B - Ifter Party——分解因子

题意

有C个人,然后给他们P个食物,每个人吃Q个,然后剩下L个,给定P和L,Q > L求Q可能的情况。

思路
本题就是分解Q-L的因子,升序输出其中大于L的因子,分解因子的时候要特别注意到因子刚好是开方的情况。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;

const int N = 2e5;
int cas;
ll p, l;
ll ans[N], cnt = 0;

void divide(int n) {
   
    for(int i = 1; i <= sqrt(n); i++) {
   
        if(n % i == 0) {
   
            if(i > l)
                ans[cnt++] = i;
            if(n / i > l && i * i != n)
                ans[cnt++] = n / i;
        }
    }
}
int main() {
   
    scanf("%d", &cas);
    for(int icas = 1; icas <= cas; icas++) {
   
        cnt = 0;
        scanf("%lld %lld", &p, &l);
        divide(p - l);
        printf("Case %d:", icas);
        if(cnt == 0) {
   
            printf(" impossible\n");
        } else {
   
            sort(ans,ans + cnt);
            for(int i = 0 ; i < cnt; i++)
                printf(" %lld", ans[i]);
            puts("");
        }
    }
    return 0;
}
C - Eid

题意

求给定的 n n n个数的LCM。

思路
利用唯一分解定理的一个性质:
令: a = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p m c m a = p_1^{c_1}*p_2^{c_2}*...*p_m^{c_m} a=p1c1p2c2...pmcm
   b = p 1 d 1 ∗ p 2 d 2 ∗ . . . ∗ p m d m b = p_1^{d_1}*p_2^{d_2}*...*p_m^{d_m} b=p1d1p2d2...pmdm

l c m ( a , b ) = p 1 m a x ( c 1 , d 1 ) ∗ p 2 m a x ( c 2 , d 2 ) ∗ . . . ∗ p m m a x ( c m , d m ) lcm(a,b) = p_1^{max(c_1,d_1)}* p_2^{max(c_2,d_2)}*...* p_m^{max(c_m,d_m)} lcm(a,b)=p1max(c1,d1)p2max(c2,d2)...pmmax(cm,dm)

所以直接找出每个质数因子的最高次幂,然后求所有每个质因子最高次幂的乘积即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<sstream>
#include<cmath>
#define ll long long
using namespace std;

const int N = 10010;
int m, cas, a,len;
int prime[N], prime_tot = 0, p[N], c[N],tot = 0,ans[N];
bool prime_tag[N],bk[N];

void get_prime() {
   
    for(int i = 2 ; i < N; i++) prime_tag[i] = true;
    for(ll i = 2; i < N; i++) {
   
        for(int j = i * i; j < N; j += i) {
   
            prime_tag[j] = false;
        }
    }
    for(int i = 2; i < N; i++)
        if(prime_tag[i]) prime[prime_tot++] = i;
}

void mul(int x) {
   
	int c=0;
	for(int i=0; i<len; i++) {
   
		int k=ans[i]*x+c;
		c=k/10;
		ans[i]=k%10;
		if(i==len-1&&c) {
   
			//ans[len]=c;
			len++;
		}
	}
}
void divide(int n) {
   
    for(int i = 0 ; i < prime_tot && prime[i] <= n; i++) {
   
        int tmp = 0;
        if(n % prime[i] == 0) {
   
            if(!bk[prime[i]]){
   
                p[tot++] = prime[i];
                bk[prime[i]] = true;
            }
            while(n % prime[i] == 0 && n > 1) {
   
                tmp++;
                n /= prime[i];
            }
            c[prime[i]] = max(c[prime[i]], tmp);
            if(n == 1) break;
        }
    }
    if(n > 1) {
   
        p[tot++] = n;
        c[n] = max(c[n], 1);
    }
}
int qpow(int a, int b) {
   
    int ret = 1;
    while(b) {
   
        if(b & 1) {
   
            ret *= a;
        }
        a *= a;
        b >>= 1;
    }
    return ret;
}
int main() {
   
    ios::sync_with_stdio(false);
    get_prime();
    int icas = 1;
    cin >> cas;
    //scanf("%d", &cas);
    while(cas--) {
   
        tot = 0;
        memset(p, 0, sizeof(p));
        memset(c, 0, sizeof(c));
        memset(bk, 0, sizeof(bk));
        memset(ans, 0, sizeof(ans));
        cin >> m;
        //scanf("%d",&m);
        for(int i = 0; i < m; i++) {
   
            cin >> a;
            //scanf("%d",&a);
            divide(a);
        }
        ans[0] = 1;
        len = 1;
        for(int i = 0; i < tot; i++) {
   
            int num = qpow(p[i],c[p[i]]);
            mul(num);
        }
		cout << "Case " << icas++ << ": " ;
		for(int j = len - 1; j >= 0; j--)
			cout << ans[j];
		cout << "\n";
    }

    return 0;
}
/*
3
5
1 2 3 5 7
3
2 20 10
4
5 6 30 60

*/

D - Trailing Zeroes (I) ——唯一分解定理推论求因数个数

题意

给一个数,求出除1以外的因子的个数。

思路
利用唯一分解定理的一个推论:

N N N的正约数个数为( ∏ \prod 表示连乘)
( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c m + 1 ) = ∏ i = 1 m ( c i + 1 ) (c_1+1)*(c_2+1)*...*(c_m+1)=\displaystyle\prod^m_{i=1} (c_i+1) (c1+1)(c2+1)...(cm+1)=i=1m(ci+1)

求出 N N N的约数个数以后再减去1即可。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;

const int N = 1e6 + 10;
ll cas,n;
int prime[N],prime_tot = 0;
bool prime_tag[N] = {
   0};
void get_prime() {
   
	for(int i = 2; i < N; i++) {
   
		if(!prime_tag[i]) {
   
			prime[prime_tot++] = i;
		}
		for(int j = 0 ; j < prime_tot && i * prime[j] < N; j++) {
   
			prime_tag[i * prime[j]] = true;
			if(i % prime[j] == 0)
				break;
		}
	}
}

ll solve(ll n) {
   
	ll res = 1;
	for(int i = 0 ; i < prime_tot && prime[i] * prime[i] <= n; i++) {
   
		if(n % prime[i] == 0) {
   
			ll c = 0;
			while(n % prime[i] == 0 && n > 1) {
   
				c++;
				n /= prime[i];
			}
			res = res * (c + 1);
			if(n == 1)
				break;
		}

	}
	if(n > 1)
		res = res * 2;
	return res - 1;
}

int main() {
   
	int icas = 1;
	get_prime();
	scanf("%lld",&cas);
	while(cas--) {
   
		scanf("%lld",&n);
		printf("Case %d: %lld\n",icas++,solve(n));
	}

	return 0;
}
E - Intelligent Factorial Factorization——唯一分解定理分解质因子

题意

给一个 N N N,将 N ! N! N分解质因子。

思路

将数据范围内所有的数分解质因子,将质因子和幂储存再数组里,然后求一个前缀和就等于是将阶乘分解质因子。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;

const int N = 110;
int p[N][N], c[N][N] = {
   0}, prime[N],ans[N][N] = {
   0};
int p_cnt = 0, prime_tot = 0;
bool prime_tag[N];
int n, cas, icas = 1;

void get_prime() {
   
    for(int i = 1; i < N; i++) prime_tag[i] = true;
    for(int i = 2; i < N; i++) {
   
        for(ll j = i * i ; j < N; j += i)
            prime_tag[j] = false;
    }
    for(int i = 2; i < N; i++)
        if(prime_tag[i]) prime[prime_tot++] = i;
}
void divide(int m) {
   
    p_cnt = 0;
    int up = m;
    for(int i = 0 ; i < prime_tot && prime[i] * prime[i] <= up; i++){
   
        if(m % prime[i] == 0){
   
            p[up][p_cnt++] = prime[i];
             c[up][prime[i]] = 0;
            while(m % prime[i] == 0 && m > 1){
   
                c[up][prime[i]]++;
                m /= prime[i];
            }
            if(m == 1)
                break;
        }
    }
    if(m > 1)
        p[up][p_cnt++] = m,c[up][m] = 1;
}
void  solve(){
   
    for(int i = 2; i <= 100; i++){
   
        for(int j = 0; j < prime_tot; j++){
   
            ans[i][prime[j]] = ans[i - 1][prime[j]] + c[i][prime[j]];
        }
    }
}
int main() {
   
    get_prime();
    for(int i = 2; i <= 100; i++)
        divide(i);
    solve();
    scanf("%d", &cas);
    while(cas--) {
   
        scanf("%d", &n);
        printf("Case %d: %d = ",icas++,n);
        int first = 1;
        for(int i = 0 ; i < prime_tot; i++){
   
            if(ans[n][prime[i]]){
   
                if(first){
   
                    printf("%d (%d)",prime[i],ans[n][prime[i]]);
                    first = 0;
                }else{
   
                    printf(" * %d (%d)",prime[i],ans[n][prime[i]]);
                }
            }
        }
        printf("\n");
    }
    return 0;
}
F - Digits of Factorial ——对数性质

题意

n ! n! n! m m m 进制下有几位数

思路
首先可以发现一个规律,对于一个数 K K K,它在 m m m 进制下的数位个数就是 l o g m K log_mK logmK

那么我们要求的答案就是 a n s = l o g m   n ! ans =log_m\ n! ans=logm n!

根据对数的运算规律
a n s = l o g m   n ! = l o g m 1 + l o g m 2 + . . . + l o g m n ans =log_m\ n!=log_m1+log_m2+...+log_mn ans=logm n!=logm1+logm2+...+logmn

a n s = l o g ( 1 ) + l o g ( 2 )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值