【问题标题】:Using divide and conquer使用分而治之
【发布时间】:2014-10-20 07:04:14
【问题描述】:

所以,我有一个问题。

输入包含两个数字NMN 基本上告诉我们将出现的1 的数量,M 是我们除以并返回余数的数字。

1≤N≤10^16
2≤M≤10^9

Sample input:
3 3
4 7
5 18

Sample output:
0
5   
5

解释:

111 % 3 = 0
1111 % 7 = 5
11111%18 = 5 

时间限制:最多 1 秒。

由于输入非常大,我显然不能使用模运算符。我在考虑按位运算符,但这也不会让我受到时间限制。有任何想法吗?谢谢!

【问题讨论】:

  • 如果你想得到答案,请把这个移到打码

标签: c divide-and-conquer


【解决方案1】:

我希望这不是 Project Euler 或其他类似的网站。

首先将数字1111... 命名为Repunit

作为modular arithmetic 状态:

如果a1 = b1 mod na2 = b2 mod n 那么:

a1 + a2 = b1 + b2 mod n
a1 - a2 = b1 - b2 mod n
a1 * a2 = b1 * b2 mod n

size n 的重复单位可以以 10 为底写为:1*10^n + 1*10^(n-1) ... + 1*10 + 1

这将是1*10^n + 1*10^(n-1) ... + 1*10 + 1 = X mod n

1 = x1 mod n
x1*10 + 1 = x2 mod n
x2*10 + 1 = x3 mod n
...
x(n-1)*10 + 1 = X mod n

最后的模块化结果将是解决方案:

C++ 代码,Steve Cox 提出的更新代码:

#include <iostream>
#include <map>
#include <cmath>

long long repunit_module(long long repunit_size, long long n) {
    if (repunit_size < 100) {
        // Calculate normally
        long long module_value = 1;
        for (long long i = 2; i <= repunit_size; i++) {
            module_value = (module_value * 10 + 1) % n;
        }
        return module_value;
    } else {
        // x(2n) = (x(n+1) - x(n) + 1) * x(n)
        long long xn = repunit_module(repunit_size / 2, n); // x(n) mod n
        long long xn1 = (xn * 10 + 1) % n; // x(n+1) mod n
        long long rest = xn1 - xn + 1; 
        if (rest < 0) // normalyze for module
            rest += n;
        long long result = ((rest % n) * xn) % n;

        if (repunit_size % 2 == 1) { // if size is 2n + 1 calc the last
            return (result * 10 + 1) % n;
        } else { // if size is 
            return result;
        }
    }    
}

int main() {
    long long rps = std::pow(10, 16);
    std::cout << repunit_module(3, 3) << std::endl;
    std::cout << repunit_module(4, 7) << std::endl;
    std::cout << repunit_module(5, 18) << std::endl;
    std::cout << repunit_module(rps, 123456789) << std::endl;
}

【讨论】:

  • 这里有一个提示x(2n) = (x(n+1) - x(n) + 1) * x(n),可以让你快速跳过
  • 或者以最不可读的方式,注意111111 = 111 * (1111 - 111 + 1)
【解决方案2】:

好吧,NetVipeC 的回答很快就变得丑陋了

这个答案基于三个身份

x(1) = 1
x(n) = x(n-1) * 10 + 1
x(2n) = x(n)*(x(n)*9 + 2)

这些也适用于 Z_M。

我自下而上编写了这些标识,但实现是自上而下的,因为它非常简单。

#include <stdio.h>

long long modulo(long long n, long long m) {
    if (n == 1) return 1;
    long long mod = modulo(n / 2, m);
    long long whole = ((mod * 9 + 2) * mod) % m;
    if(n & 1)
        return (whole * 10 + 1) % m;
    return whole;
}

int main() {
    printf("%ld\n", modulo(3, 3));
    printf("%ld\n", modulo(4, 7));
    printf("%ld\n", modulo(5, 18));
    printf("%ld\n", modulo(1e6, 123456789));
    printf("%ld\n", modulo(1e15, 123456789));
}

输出:

time ./tst
0
5
5
1239742
93889873
./tst  0.00s user 0.00s system 56% cpu 0.002 total

【讨论】:

  • 太棒了。但是,就像我是初学者一样,我的意思是,你是怎么想到的?你是怎样做的?这真的很棒。我怎样才能学会这样做?
  • 这个我刚刚使用了费曼算法。但答案来自离散数学和动态规划。
猜你喜欢
  • 2013-11-09
  • 2013-01-31
  • 2012-03-04
  • 2011-05-22
  • 2014-12-12
  • 2015-01-25
  • 1970-01-01
  • 2020-05-31
  • 2017-12-05
相关资源
最近更新 更多