1. 程式人生 > >位運算_CH0102_64位整數乘法

位運算_CH0102_64位整數乘法

思路分析:給出兩種方法, 法1基於遞推, 法2利用C++基本資料型別的性質

法1:

考慮b的二進位制表示, (a * b) mod p = (a * (c_{k}2^{k} +...+ c_{0}2^{0})) mod p = (c_{k}a2^{k}+...+c_{0}a2^{0}) mod p

又: a2^{i} = 2 * a2^{i - 1} (i >= 1), 則可先求出s = a2^{i - 1} mod\: p, 且s < p <= 10^{18}, 可直接使用語句: s * 2 % p 計算a2^{i}\: mod\: p

至此,首先計算a2^{0}\: mod\: p, 然後遞推即可, AC程式碼如下:

//CH0102_64位整數乘法
#include <iostream>
#include <cstdio>
using namespace std;
//返回(a * b) mod p的值, 要求:1 <= a, b, p <= 10^18
long long getVal(long long a, long long b, long long p){
	long long ans = 0, tmp = a % p;
	for(; b; b >>= 1, tmp = tmp * 2 % p) if(b & 1) ans = (ans + tmp) % p;
	return ans;	
} 
int main(){
	long long a, b, p;
	scanf("%lld %lld %lld", &a, &b, &p);
	cout << getVal(a, b, p) << endl;
	return 0;
}

法2:

由 (a * b) mod p = a * b - \left \lfloor \frac{a * b}{p} \right \rfloor * p,  且該等式兩端的值小於2^{64}, 可先計算s = \left \lfloor \frac{a * b}{p} \right \rfloor的值, 如何計算s? 考慮到s <= 10^{18}, 利用C++中long double的有效數字為18~19位, 因此對a, b, p使用long double計算a * b / p的值, 對結果取整數部分即為s的值.繼而將a, b, s, p均定義為unsigned long long 型別, 依據C++中unsigned long long運算性質, 表示式a * b - s * p的值為其數學上的實際值對2^{64}取模, 因為其實際值小於2^{64}, 故表示式a * b - s * p 為其實際值, 具體步驟如下AC程式碼所示:

//CH0102_64位整數乘法
#include <iostream>
#include <cstdio>
using namespace std;
typedef unsigned long long ull; 
//返回(a * b) mod p的值, 要求:1 <= a, b, p <= 10^18
ull getVal(ull a, ull b, ull p){
	return a * b - (ull)((long double)a * b / p) * p;
} 
int main(){
	long long a, b, p;
	scanf("%lld %lld %lld", &a, &b, &p);
	cout << getVal(a, b, p);
	return 0;
}