POJ_1091 跳蚤

题目描述

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=1Mαii=1

求其中 αi 的整数解的个数。
这是个一个柏拉图方程。而柏拉图方程有整数解的充要条件如下:

一次不定方程是形式如

anxn=c

的方程,一次不定方程有整數解的充要條件為:

gcd(a1,...,an)|c

引自维基 丟番圖方程

那么,我们可以知道能达到结果的整数解的最大公约数必然为1!!!
那么,我们可以知道不满足的组合的最大公约数必然不为1!!!
那么,因为最后一个数字是确定的就是M,所有不满足的组合的最大公约数必然是M的质因子的倍数。
所以,我们只要去掉这些组合就可以嘿嘿嘿了。


如何计算需要去掉的组合

根据排列组合的公式,我们可以知道
需要去掉的组合为

i((1)i1j(MpSi)N)

其中 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 都是不可取的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值