kuangbin带你飞数论

本文探讨了在编程竞赛中如何利用数学和算法解决问题,包括计算N的阶乘在k进制下的位数、求解末尾零个数、连续整数之和的奇因子个数以及求解最大LCM。通过解析题目思路,展示了数学在编程中的重要性和应用技巧,如对数、质因数分解和进制转换等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LightOj 1045 Digits of Factorial

题意(好题)

给定一个数N,和进制k, 求N!在k进制下 位数的个数

思路

设进制为base,m为个数
N ! = b a s e m N! = base^m N!=basem, m m m即 位数的个数

b a s e m = N ! = 1 ∗ 2 ∗ 3 ∗ 4 ∗ . . . ∗ ( n − 1 ) ∗ n base^m = N! = 1 * 2 * 3 * 4 * ...* (n - 1) * n basem=N!=1234...(n1)n
两边同时取对数log
m l o g b a s e = l o g 1 + 1 o g 2 + 1 o g 3 + . . . + l o g n mlog^{base} = log1 + 1og2 + 1og3 + ..._+ logn mlogbase=log1+1og2+1og3+...+logn
m = l o g 1 + l o g 2 + l o g 3 + . . . + l o g n l o g b a s e m = \frac{log1+log2+log3+...+logn}{log^{base}} m=logbaselog1+log2+log3+...+logn
注意算的结果是下取整,则当结果m并不是恰好的数时,即存在小数的话,需要加 1 1 1

补充一下进制转换:
120转换成八进制

120 % 8 = 0 120 \% 8 = 0 120%8=0, 120 / 8 = 15 120 / 8 = 15 120/8=15
15 % 8 = 7 15 \% 8 = 7 15%8=7, 15 / 8 = 1 15 / 8 = 1 15/8=1
1 % 8 = 1 1 \% 8 = 1 1%8=1, 1 / 8 = 0 1 / 8 = 0 1/8=0

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const int N = 1000010;

double s[N];

void init() {
    s[0] = s[1] = 0;
    for (int i = 2; i < N; i ++)
        s[i] = s[i - 1] + log((double)i);
}
int main() {
    init();
    int T;
    int id = 0;
    scanf("%d", &T);
    while(T --) {
        int n, base;
        scanf("%d%d", &n, &base);
        if(!n) {
            printf("Case %d: %d\n", ++id, 1);
            continue;
        }
        double c = s[n];
        double m1 = c / (log((double)base));
        int m2 = c / (log((double)base));
        if(m2 != (int)ceil(m1)) {
            m2 ++;
        }
        printf("Case %d: %d\n", ++ id, m2);
    }
    return 0;
}

LOJ1090 Trailing Zeroes (II)

题意

输出结果末尾0的个数

思路

找质因数分解最终2和5的幂个数就行,2和5的幂 取最小值

C n r = n ! r ! ( n − r ) ! C_n^r = \frac{n!}{r!(n-r)!} Cnr=r!(nr)!n!
然后阶乘分解+质因子分解就行

代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long LL;
LL T, n, r, p, q;

LL get_num1(LL n, int x) {
    LL s = 0;
    for (LL j = n; j; j /= x) {
        s += j / x;
    }
    return s;
}

LL get_num2(LL n, LL x) {
    LL s = 0;
    while(n % x == 0) {
        s++;
        n /= x;
    }
    return s;
}
int main() {
    
    int id = 0;
    cin >> T;
    while(T --) {
        cin >> n >> r >> p >> q;
        int num_2 = get_num1(n, 2);
        int num_5 = get_num1(n, 5);
        num_2 -= get_num1(r, 2);
        num_5 -= get_num1(r, 5);
        num_2 -= get_num1((n-r), 2);
        num_5 -= get_num1((n-r), 5);

        num_2 += (LL)get_num2(p, 2) * q;
        num_5 += (LL)get_num2(p, 5) * q;
        LL ans;
        if(num_2 > num_5) {
            ans = num_5;
        }
        else
            ans = num_2;
        printf("Case %d: %lld\n", ++id, ans);
    }

    return 0;
}

LightOJ - 1278 Sum of Consecutive Integers

思路

n = a 1 + a 2 + a 3 + . . . + a m n = a_1 + a_2 + a_3 + ...+a_m n=a1+a2+a3+...+am
n = m ∗ a 1 + ( a 1 + m − 1 ) 2 n = m * \frac{a_1+(a_1+m-1)}{2} n=m2a1+(a1+m1)
a 1 = n m − m − 1 2 a_1=\frac{n}{m}-\frac{m-1}{2} a1=mn2m1
可知 m , n , a 1 m,n,a_1 m,n,a1均为整数,则从 m − 1 2 \frac{m-1}{2} 2m1 看出 m m m一定 是奇数
n m \frac{n}{m} mn看出 m m m n n n的因子, 则 m m m n n n的奇因子

因此,求出 n n n的所有奇因子个数即可

注意:最后要减去一,因为 m m m不能为1

代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 10000010;

int primes[N];
bool st[N];
int cnt;

void get_primes() {
    for (int i = 2; i < N; i ++) {
        if(!st[i])
            primes[cnt++] = i;
        for (int j = 0; primes[j] * i < N; j ++) {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0)
                break;
        }
    }
}

int main() {
    get_primes();
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    int id = 0;
    int T;
    scanf("%d", &T);
    
    while(T --) {
        long long n;
        scanf("%lld", &n);
        while(n % 2 == 0)
            n /= 2;
        long long ans = 1;
        for (int i = 0; i < cnt && primes[i] * primes[i] <= n; i ++) {
            int p = primes[i];
            if(n % p == 0) {
                int s = 0;
                while(n % p == 0) {
                    s++;
                    n /= p;
                }
                ans = (long long)ans * (s + 1);
            }
        }

        if(n != 1)
            ans *= 2;
        printf("Case %d: %d\n", ++id, ans - 1);
    }

    return 0;
}

LightOJ - 1289 LCM from 1 to n

题意

求LCM(1,2,3…,n)

思路

最大公倍数无非就是 p 1 m a x ( c 1 ) p 2 m a x ( c 2 ) . . . p n m a x ( c n ) p_1^{max(c1)}p_2^{max(c2)}...p_n^{max(c_n)} p1max(c1)p2max(c2)...pnmax(cn)

步骤:

  1. 筛质数(st用二进制存),预处理前缀和(乘法)
  2. 分解质因数, 只需枚举到根号n (大于根号n的幂都是1), 求 m a x ( c 1 ) max(c_1) max(c1)
  3. 相乘

注意

  1. 取模 2 3 2 2^32 232, 用unsigned int 就行
  2. 1~n的质数个数为 n ln ⁡ n \frac{n}{\ln n} lnnn

代码

#include<bits/stdc++.h>

using namespace std;

const int N = 1e8 + 5, M = 5800000;
// 因为st只需0,1状态, 可以用位图即二进制位存储状态, 否则超内存
bitset<N> st;
int primes[M];
int cnt;
unsigned int sum[N];

void get_primes() {
    for (int i = 2; i < N; i ++) {
        if(!st[i])
            primes[cnt++] = i;
        for (int j = 0; primes[j] * i < N; j ++) {
            st[primes[j] * i] = 1;
            if(i % primes[j] == 0)
                break;
        }
    }

	// 预处理前缀乘
	sum[0] = 2;
	for (int i = 1; i < cnt; i ++)
		sum[i] = sum[i - 1] * primes[i];
}

unsigned int qmi(int a, int b) {
	unsigned int ans = 1;
	while(b) {
		if(b & 1)
			ans *= a;
		a *= a;
		b >>= 1;
	}
	return ans;
}
int main() {
	get_primes();

	int T;
	scanf("%d", &T);
	int id = 0;
	while(T --) {
		int n;
		scanf("%d", &n);

		// 找第一个大于n的素数, 然后减一, 就是小于n的最大素数了
		int pos = upper_bound(primes, primes + cnt, n) - primes;
		pos -= 1;

		unsigned int ans = sum[pos];

		for (int i = 0; i < cnt && primes[i] * primes[i] <= n; i ++) {
			int s = 0;
			int temp = n;
			while(temp / primes[i]) {
				s++;
				temp /= primes[i];
			}
			s--; // 前缀和已经乘了一次, 要减去一次

			ans = ans * qmi(primes[i], s);
		}

		printf("Case %d: %lld\n", ++id, ans);
	}

	return 0;
}

LightOj1340 Story of Tomisu Ghost

题意

问n!在b进制下至少有t个后缀零,求最大的b。

思路

由转换进制,辗除法可知,看质因子最大的幂即可
p k c k p_k^{c_k} pkck c k > = t c_k >= t ck>=t, 则该 p k p_k pk满足,且 c k c_k ck很大的话,可以继续扩大b进制,即答案为 p i s / t p_i^{s/t} pis/t进制

代码(转Lweleth)


 
#include<bits/stdc++.h>
#define LL long long
#define PII pair
#define MP(x, y) make_pair((x),(y))
#define fi first
#define se second
#define PB(x) push_back((x))
#define MMG(x) memset((x), -1,sizeof(x))
#define MMF(x) memset((x),0,sizeof(x))
#define MMI(x) memset((x), INF, sizeof(x))
using namespace std;
 
const int INF = 0x3f3f3f3f;
const int N = 1e5+2000;
const int mod = 10000019;
LL pri[N];
int c = 0;
bool vis[N];
 
void prime()
{
    for(int i = 2; i < N; i++)
    {
        if(!vis[i])
        {
            for(int j = i + i; j < N; j+= i)
            {
                if(!vis[j])
                    vis[j] = 1;
            }
            pri[c++] = i;
        }
    }
}
 
LL fpow(LL a, LL n)
{
    LL r = 1;
    while(n > 0)
    {
        if(n & 1)
            r = r * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return r;
}
 
int main()
{
    prime();
    int T;
    int cnt = 0;
    cin >> T;
    while(T--)
    {
        LL n;
        LL r;
        cin >> n >> r;
        LL ans = 1;
        for(int i = 0; i < c && pri[i] <= n; i++)
        {
            LL t = n;
            LL ct = 0;
            while(t)
            {
                ct += t / pri[i];
                t /= pri[i];
            }
            if(ct >= r)
                ans = ans * fpow(pri[i], ct/r) % mod;
            if(ct < r)
                break;
        }
        if(ans == 1)
            printf("Case %d: -1\n", ++cnt);
        else
            printf("Case %d: %d\n", ++cnt, ans);
    }
    return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值