http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=6443#problem/J
分数取模问题:
今天在数论方面我得反思一下;还是太弱,在这方面必须加强学习及题目的练习
题目如上,这个解题思路是很好想的,不过最后由于知识的遗漏使我们不能过了此题。题目让我们求的是:用不大于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;
}
#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逆元可以下岗了!!!!!!!