China Northeast Multi-University Training Contest I J - Remoteland

本文探讨了一个模运算问题,涉及数论基础知识,包括寻找最大可能的完全平方数,利用筛素方法和快速幂算法进行求解。特别强调了在数据范围内如何优化算法以避免超时,并通过实例展示了求解过程。文章还揭示了一个关键数学结论,即对于素数模运算,可以直接通过模乘和幂运算来代替逆元操作,简化了计算过程。

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

http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=6443#problem/J

分数取模问题:

今天在数论方面我得反思一下;还是太弱,在这方面必须加强学习及题目的练习

Description

Download as PDF
In the Republic of Remoteland, the people celebrate their independence day every year. However, as it was a long long time ago, nobody can remember when it was exactly. The only thing people can remember is that today, the number of days elapsed since their independence ( D) is a perfect square, and moreover it is the largest possible such number one can form as a product of distinct numbers less than or equal to  n.

As the years in Remoteland have 1, 000, 000, 007 days, their citizens just need D modulo 1, 000, 000, 007. Note that they are interested in the largest D, not in the largest D modulo 1, 000, 000, 007.

Input

Every test case is described by a single line with an integer n, ( 1$ \le$n$ \le$10, 000, 000). The input ends with a line containing 0.

Output

For each test case, output the number of days ago the Republic became independent, modulo 1, 000, 000, 007, one per line.

Sample Input

4
9348095
6297540
0

Sample Output

4
177582252
644064736
题目如上,这个解题思路是很好想的,不过最后由于知识的遗漏使我们不能过了此题。

题目让我们求的是:用不大于n内的所有数去组成一个尽可能大的完全平方数。

我们现在不过于考虑其他的问题,那么我们很容易想到是求出n!的所有的素数因子的个数,我们选取他们每个最多的偶数个。即如果是奇数个舍掉一个因子,偶数个全要了。
那么得出的乘积绝对是最大的。但是符不符合题意呢?我们担心的是这偶数个因子不一定来自一个数啊!这个仔细想一下会发现,这个是不用担心的,我们偶数个全要,奇数个我们去掉一个,现在我们考虑的是去掉的那些数是否是独立的。如果是独立的那么我们求的结果就符合题意。事实上,由于每个素数最多去掉一个当然拿的出且都是独立的。
那么我们的解题思路就很清晰了:
1.看一下数据范围10000000,那么我们采用筛素的方法也是很快的,在这里我不得不检讨一下,这方面的模版必须准备好的。这个解决!
2.求n!中含有任意一个数的因子个数函数这个是很好求的。这个解决!
3.很明显要用快速幂,但是那所有的素数都调用函数快速幂的求发现是超时的,难道有更好的解法吗?回答是有的。
普通筛素会超时的,必须在原来的基础上的筛素才勉强过:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef int LL;
const int MAX = 10000011;
bool Isprime[MAX];
LL prime[MAX];
LL p_num[MAX];
long long FAC[MAX];
int p_size;
int p_;
const LL mod = 1000000007;


/*
 * 题目是让你用不大于n的数,
 * (每个数只能用一次)组成
 * 一个尽可能大的平方数。我
 * 们先不要考虑互不相同这个条件,
 * 实际就是找出n!里,能找出最大的
 * 平方数因子。我们采取的先把
 * n!以内的所有因子找出来,然后
 * 求所有因子的个数,偶数全要,奇
 * 数的取偶即可。
 */


void get_prime() {
    p_size = 0;
    memset(Isprime, false, sizeof (Isprime));
    for (int i = 2; i < MAX; i++) {
        if (!Isprime[i]) {
            prime[p_size++] = i;
        }
        for (int j = 0, t; j < p_size && (t = prime[j] * i) < MAX; j++) {
            Isprime[t] = true;
            if (i % prime[j] == 0)break;
        }
    }


    FAC[0] = 1;
    for (int i = 1; i < MAX; i++) {
        FAC[i] = FAC[i - 1] * i % mod;
    }
}


void get_p_num(LL n) {
    int i;
    for (i = 0; i < p_size && prime[i] <= n; i++) {
        LL res = 0;
        LL temp = n;
        while (temp >= prime[i]) {
            res += temp / prime[i];
            temp /= prime[i];
        }
        p_num[i] = res;
    }
    p_ = i;
}


long long quick(LL base, LL p) {
    long long temp = base;
    long long res = 1;
    while (p) {
        if (p & 1) {
            res = res * temp % mod;
        }
        temp = temp * temp % mod;
        p >>= 1;
    }
    return res;
}


long long get_ans(LL n) {
    long long Mod = 1;
    for (int i = 0; i < p_; i++) {
        if (p_num[i] & 1) {
            Mod = Mod * prime[i] % mod;
        }
    }
    return FAC[n] * quick(Mod, mod - 2) % mod;
}


int main() {
    get_prime();
    LL n;
    while (scanf("%d", &n)) {
        if (n == 0) break;
        get_p_num(n);
        printf("%lld\n", get_ans(n));
    }
    return 0;
}

现在我们抽象出他的解题思路:
如果a*b=c; mod为素数
A=a%mod; B=b%mod;C=c%mod;
现在我们知道了:B、C求A
这是个神奇的结论:(他都可以代替逆元而解决逆元的问题)
a%mod=c/b %mod;
我们想着用逆元,可是我们无法求b的逆元;
我们只知道:C=c%mod,B=b%mod
A=a%mod=(a*1)%mod=(a%mod)*(1%mod)%mod=(a%mod)*(b^(mod-1)%mod)%mod(有mod为素数和费马小定理b^(mod-1)%mod=1)
=[a*b^(mod-1)]%mod=(a*b)*(b^(mod-2))%mod=c*(b^(mod-2))%mod=(c*mod)*((b%mod)^(mod-2)%mod)%mod=C*B^(mod-2)%mod;

现在我们得到一个重要的结论就是:
(p为素数)a/b%p=(a%p)*(b%p)^(p-2)%p;
那么此类题目全部OK逆元可以下岗了!!!!!!!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值