这个问题实际上来自一个名为codechef的竞争编程网站。
问题如下。
给出整数A,B和N,您应该计算A ^ N + B ^ N的GCD和 | AB |。 (假设对于任何正整数a,GCD(0,a)= a)。从此 数字可能非常大,以1000000007(109 + 7)为模进行计算。
完整的问题是here
值约束如下:
1 <= A,B,N <= 1e9(子任务#1)
1 <= A,B,N <= 1e12(子任务#2)
B <=总是A
我的解决方案通过了第一个子任务,但是当A,B,N的值很大时,第二次失败。
这是我的解决方案:
#include <bitset>
#include <iostream>
using std::bitset;
using std::cin;
using std::cout;
typedef unsigned long long ull;
constexpr size_t bit_size = sizeof(ull) * 8;
ull modular_exponetiation(ull a, bitset<bit_size> n, ull mod) {
ull c = 0;
ull d = 1;
for (int t = n.size() - 1; t >= 0; t--) {
c *= 2;
d = ((d % mod) * (d % mod)) % mod; //(d*d)%mod
if (n[t] == 1) {
c++;
d = ((d % mod) * (a % mod)) % mod; //(d*a)%mod
}
}
return d;
}
ull euclid_gcd(ull a, ull b) {
if (b == 0)
return a;
else
return euclid_gcd(b, a % b);
}
int main() {
int test;
cin >> test;
while (test--) {
ull a, b, n;
cin >> a >> b >> n;
ull modder = a - b;
if (modder != 0) {
ull out_res = 0;
bitset<bit_size> bin_rip(n);
ull first_mod_res = (modular_exponetiation(a, bin_rip, modder) +
modular_exponetiation(b, bin_rip, modder)) %
modder;
if (first_mod_res == 0)
out_res = modder;
else
out_res = euclid_gcd(modder, first_mod_res);
cout << out_res % 1000000007 << std::endl;
} else {
// mod by 0 is not defined using the problem defined result.
// GCD(0,a) == a;
ull modder = 1000000007;
bitset<bit_size> bin_rip(n);
ull res = (modular_exponetiation(a, bin_rip, modder) +
modular_exponetiation(b, bin_rip, modder)) %
modder;
cout << res << std::endl;
}
}
return 0;
}
这不是家庭作业,我也不想要精确的答案或代码更正。我确实理解所有这些内容,但不明白为什么在更大的值上它会失败?
任何方向或提示都会有用。
答案 0 :(得分:3)
如果modder
= 1e12,则您的取模不起作用。原因1e12 * 1e12。会有溢出。
查看this以进行模运算而不会溢出。
您可以尝试一下。这里的乘法是通过求和完成的。
long long multiply(long long a,long long b,long long m){
if(b == 0){
return 0;
}
if(b==1){
return a%m;
}
if(b&1){
return ((a%m)+multiply(a,b-1,m))%m;
}
long long x = multiply(a,b>>1,m);
return multiply(x,2,m);
}
long long bigmod(long long a,long long b, long long m){
if(b == 0){
return 1;
}
if(b == 1){
return a%m;
}
if(b & 1){
return multiply(a%m,bigmod(a,b-1,m),m);
}
long long x = bigmod(a,b>>1,m);
return multiply(x,x,m);
}
答案 1 :(得分:1)
特别感谢萨吉布指出问题。
在这里,我会自己回答,以便更好地理解。
d = ((d % mod) * (d % mod)) % mod;
d = ((d % mod) * (a % mod)) % mod;
是这两行中断并导致溢出。所有积分都归@sajib所有。
他还指出了解决此问题的正确方法(与求和)。我很高兴了解他的代码的作用。
所以我在这里详细解释。
(a * b) % m
如果a
和b
都是非常长的long long值,则将导致溢出。
幼稚的方法是使用任意精度的数据类型,例如python中的int或Java中的Biginteger类。但是,这种方法不会奏效,因为将字符串内部转换为int然后执行操作将导致二进制数系统中加法和乘法的计算变慢。
有效的解决方案:由于a和b可能非常大,所以如果我们尝试直接相乘,则肯定会溢出。因此,我们使用乘法的基本方法,即
a * b = a + a + … + a (b times)
因此我们可以轻松地计算加法的值(模m下),而无需任何 计算中溢出。但是,如果我们尝试将a的值重复加到b倍,那么对于大的b值肯定会超时,因为这种方法的时间复杂度将变为O(b)。
因此,我们以更简单的方式将a
的上述步骤分开,即
If b is even then
a * b = 2 * a * (b / 2),
otherwise
a * b = a + a * (b - 1)
实现如下:
// Returns (a * b) % mod
long long moduloMultiplication(long long a,
long long b,
long long mod)
{
long long res = 0; // Initialize result
// Update a if it is more than
// or equal to mod
a %= mod;
while (b)
{
// If b is odd, add a with result
if (b & 1)
res = (res + a) % mod;
// Here we assume that doing 2*a
// doesn't cause overflow
a = (2 * a) % mod;
b >>= 1; // b = b / 2
}
return res;
}