题目描述
Z城市居住着很多只跳蚤。在Z城市周六生活频道有一个娱乐节目。一只跳蚤将被请上一个高空钢丝的正中央。钢丝很长,可以看作是无限长。节目主持人会给该跳蚤发一张卡片。卡片上写有N+1个自然数。其中最后一个是M,而前N个数都不超过M,卡片上允许有相同的数字。跳蚤每次可以从卡片上任意选择一个自然数S,然后向左,或向右跳S个单位长度。而他最终的任务是跳到距离他左边一个单位长度的地方,并捡起位于那里的礼物。
比如当N=2,M=18时,持有卡片(10, 15, 18)的跳蚤,就可以完成任务:他可以先向左跳10个单位长度,然后再连向左跳3次,每次15个单位长度,最后再向右连跳3次,每次18个单位长度。而持有卡片(12, 15, 18)的跳蚤,则怎么也不可能跳到距他左边一个单位长度的地方。
当确定N和M后,显然一共有 MN 张不同的卡片。现在的问题是,在这所有的卡片中,有多少张可以完成任务。
输入
两个整数N和M(N <= 15 , M <= 100000000)。
输出
可以完成任务的卡片数。
思路
最后一个数字是确定的就是M, 整个过程可以用方程表达如下
求其中
αi
的整数解的个数。
这是个一个柏拉图方程。而柏拉图方程有整数解的充要条件如下:
一次不定方程是形式如
∑anxn=c的方程,一次不定方程有整數解的充要條件為:
gcd(a1,...,an)|c
引自维基 丟番圖方程
那么,我们可以知道能达到结果的整数解的最大公约数必然为1!!!
那么,我们可以知道不满足的组合的最大公约数必然不为1!!!
那么,因为最后一个数字是确定的就是M,所有不满足的组合的最大公约数必然是M的质因子的倍数。
所以,我们只要去掉这些组合就可以嘿嘿嘿了。
如何计算需要去掉的组合
根据排列组合的公式,我们可以知道
需要去掉的组合为
其中
Si
表示i个质因子的乘积集合。
举例, 假如M有2,3,5三个质因子,那么,
需要去掉的组合 = (有公因数2的组合)+ (有公因数3的组合)+ (有公因数5的组合)- (有公因数2,3的组合) -(有公因数2,5的组合) + (有公因数3,5的组合)
代码实现
//
// main.cpp
// POJ_1091
//
// Created by 黄成林 on 16/4/20.
// Copyright © 2016年 黄成林. All rights reserved.
//
#include <iostream>
#include <vector>
#include <string>
#include "big_int.hpp"
using namespace std;
const int MAX = 100;
vector<long long> prime_divisors;
vector<long long> combinations;
long long M;
int N;
// 求质因子
void find_all_prime_divisors(long long value) {
for (long long i = 2; i < value; ++i) {
if (0 == value%i) {
while (0 == value%i)
value /= i;
prime_divisors.push_back(i);
}
}
if (value != 1)
prime_divisors.push_back(value);
}
big_int<MAX> pow_int64(long long x, int y) {
big_int<MAX> result("1");
big_int<MAX> x_big_int(x);
for (int i = 0; i < y; ++i) {
result = result * x_big_int;
}
return result;
}
big_int<MAX> calculate(int start, int end, int index, int prime_divisor_num) {
big_int<MAX> result("0");
if (index == prime_divisor_num) {
long long temp_long_long = 1;
for (int i = 0; i < combinations.size(); ++i) {
temp_long_long *= combinations[i];
}
result = result + pow_int64(M/temp_long_long, N);
return result;
}
if (start + (prime_divisor_num-index) > end) {
return big_int<MAX>("0");
}
combinations[index] = prime_divisors[start];
result = result + calculate(start+1, end, index+1, prime_divisor_num);
result = result + calculate(start+1, end, index, prime_divisor_num);
return result;
}
void initial() {
cin >> N >> M;
find_all_prime_divisors(M);
}
int main() {
initial();
int prime_divisors_size = int(prime_divisors.size());
big_int<MAX> result = pow_int64(M, N);
for (int i = 0; i < prime_divisors_size; ++i) {
combinations.resize(i+1, 0);
if (i % 2 == 0) {
result = result - calculate(0, prime_divisors_size, 0, i+1);
} else {
result = result + calculate(0, prime_divisors_size, 0, i+1);
}
}
result.print();
return 0;
}
关于 calculate 函数
他返回公共质因子数量为prime_divisor_num的所有组合的相加;
关于在数组中计算不同的组合,请看这篇博客.
附: big_int 类
由于M可以很大, MN 用 long long也是搞不定的。所以自己写了这个big_int类,可能功能很不完整,见谅。
//
// big_int.hpp
// POJ_1091
//
// Created by 黄成林 on 16/4/20.
// Copyright © 2016年 黄成林. All rights reserved.
//
#ifndef big_int_hpp
#define big_int_hpp
#include <iostream>
#include <vector>
#include <string>
template <int MAX>
class big_int {
private:
std::vector<int> value;
int& operator[](int index);
public:
big_int();
big_int(__int64_t value_int64);
big_int(std::string value_string);
void print();
big_int<MAX> operator+ (big_int<MAX> value_add_item);
big_int<MAX> operator- (big_int<MAX> value_minus_item);
big_int<MAX> operator* (big_int<MAX> value_times_item);
big_int<MAX> operator= (big_int<MAX> big_int_item);
};
template <int MAX>
big_int<MAX>::big_int(__int64_t value_int64) {
this->value.resize(MAX, 0);
int i = MAX;
while (value_int64 / 10) {
this->value[i-1] = value_int64 % 10;
--i;
value_int64 /= 10;
}
value[i-1] = value_int64;
}
template <int MAX>
big_int<MAX>::big_int() {
this->value.resize(MAX, 0);
}
template <int MAX>
big_int<MAX>::big_int(std::string value_string) {
this->value.resize(MAX, 0);
for (int i = (int)value_string.length() - 1; i >= 0; --i) {
char c = value_string.at(i);
int value_int = c - '0';
if (value_int >= 10) return;
value[i + MAX - value_string.length()] = value_int;
}
}
template <int MAX>
void big_int<MAX>::print() {
int i = 0;
while (0 == this->value[i])
++i;
for (; i < value.size(); ++i) {
std::cout << value[i];
}
std::cout << std::endl;
}
template <int MAX>
int& big_int<MAX>::operator[](int index) {
return this->value[index];
}
template <int MAX>
big_int<MAX> big_int<MAX>::operator+ (big_int<MAX> value_add_item) {
big_int<MAX> result;
int flag = 0;
for (int i = MAX-1; i >= 0; --i) {
result[i] = value[i] + value_add_item[i] + flag;
if (result[i] >= 10) {
result[i] -= 10;
flag = 1;
} else {
flag = 0;
}
}
return result;
}
template <int MAX>
big_int<MAX> big_int<MAX>::operator- (big_int<MAX> value_minus_item) {
big_int<MAX> result;
int flag = 0;
for (int i = MAX-1; i >= 0; --i) {
result[i] = value[i] - value_minus_item[i] + flag;
if (result[i] < 0) {
result[i] += 10;
flag = -1;
} else {
flag = 0;
}
}
return result;
}
template <int MAX>
big_int<MAX> big_int<MAX>::operator* (big_int<MAX> value_times_item) {
big_int<MAX> result;
int flag = 0;
for (int i = MAX-1; i >= 0; --i)
for (int j = MAX-1; j >= 0; --j)
if (i+j+1 >= MAX)
result[i+j+1 - MAX] += value[i] * value_times_item[j];
for (int i = MAX-1; i >= 0; --i) {
result[i] += flag;
if (result[i] >= 10) {
flag = result[i]/10;
result[i] %= 10;
} else {
flag = 0;
}
}
return result;
}
template <int MAX>
big_int<MAX> big_int<MAX>::operator= (big_int<MAX> big_int_item) {
for (int i = 0; i < MAX; ++i) {
value[i] = big_int_item[i];
}
return *this;
}
#endif /* big_int_hpp */
备注
提交后,提示Time Limit Exceeded
, 但是我始终认为使用 long long 或者 __int64 都是不可取的。