题解思路:
first-My:
一:
A,B范围很大,正常数据读取不能实现,所以改用string类型读取,p为1e18,所以用longlong类型存贮,我们知道string不能直接做运算,但A可以表示:
A=
,
所以我们可以使用快速幂先减低复杂度,然后依次对每一项求%,这个样子数据每项会变小,然后利用数组存起来,然后两个for遍历A,B的值,每次都要%,但是最后做乘法时候,我们要考虑会不会爆表,因为取%后范围【0-1e18】;所以我们可以考虑使用快速乘法(类似于快速幂);然后用 unsigned long long int存储,同样在每步操作时候都要取模,最后我们直接上代码:
下面是完整思路:
A和B最大可以是两个1e5位数,无法用正常的数据类型存储,需要使用字符串存储。
用字符串存储以后也无法直接相乘,除非去模拟乘法的竖式计算,但这样不仅会TLE
模拟乘法)还会MLE(相乘以后的结果会是一个1e10位数)。
要计算的是A*B%P,而P只有1e18,可以用long long存储,所以要从P入手
A*B%P=( (A%P) * (B%P) )%P,所以可以先计算A%P和B%P虽然A和B都是用字符串存储的大数,无法直接用取余运算,但一个正整数A可以表示成
,其中n是A的位数,ai
表示A从左往右第i位上的数字,即A=i=1nai*10n-i
例如
利用公式(a+b)%p=(a%p+b%p)%p,我们可以对每个
都进行取余,再在相加的过程中取余,即可得到A%P的结果,求B%P也是同样的方法。
但要注意P有1e18,一个数a对P取余得到的结果在[0,P-1]的范围内,所以如果取余之后乘以10,就有可能超出long long的范围(约9.2e18)。
对于这种情况,可以使用unsigned long long (以下简称ull),其范围是
,最大值约为1.8e19,可以保证每次乘10取余都不会超过ull的范围。
还可以把乘10,拆分成先乘2再乘5,每次取余,这样也可以实现中间过程不超过long long的范围。
由此可以计算出A%P和B%P的结果,但最终的答案是二者相乘再对P取余。而二者相乘又可能超过long long和ull的范围,所以此时需要用到快速乘。
快速乘类似快速幂,就是把乘法拆分成更低一级的加法,每一次相加再取余不会超过ull和long long的范围,这样就可以计算出(A%P)*(B%P)%P的结果了,也就是最终的答案。
最后一步也可以使用模拟乘法来计算,因为两个数字都只有不到20位,模拟乘法最高复杂度为20*20
#include<iostream>
#include<queue>
#include<set>
#include<string>
#include<numeric>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<map>
#define int long long
using namespace std;
const int MaxN = 2e5 + 5;
int ans = 0;
int n;
int a[200001];
int b[200001];
using pii = pair<int, int>;
int gcd(int a, int b)
{
if (a == 0 || b == 0)return 0;
while (a ^= b ^= a ^= b %= a);
return b;
}
unsigned int q_mul(int a, int b, int mod)
{
unsigned int ans = 0;
while (b)
{
if (b & 1)
{
b--;
ans = (ans + a) % mod;
}
b >>= 1;
a = (a + a) % mod;
}
return ans;
}
int qpow(int a, int n, int mod)
{
int ans = 1;
while (n)
{
if (n & 01)ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
int poww[MaxN];
void solves() {
string a1, b1;
int p;
unsigned int temp1 = 0, temp2 = 0, temp = 0, a, b;
cin >> a1 >> b1 >> p;
poww[0] = 1;
for (int i = 1; i <= 100005; i++) {
poww[i] = (poww[i - 1] * 10) % p;
}
for (int i = 0; i < a1.length(); ++i)
{
temp1 = (temp1 + (a1[i] - '0') * poww[a1.length() - i - 1] % p) % p;
}
//cout<<temp1<<endl;
for (int i = 0; i < b1.length(); ++i)
{
temp2 = (temp2 % p + (b1[i] - '0') * poww[b1.length() - i - 1] % p) % p;
}
//cout<<temp2<<endl;
unsigned int res = q_mul(temp1,temp2,p)%p;
cout << res << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solves();
return 0;
}
经过oj评测我们发现最终还是在*的时候,数据域溢出了,所以本题靠此方法不可解,那么就用数据域更大的__int128吧(有些编译器没有,这个是新标准的,亲测VS2019和dev6.5都不可以,clion编译器可以成立),利用快读的方式read()和write()可以通过样例,代码如下:
#include<iostream>
#include<queue>
#include<set>
#include<string>
#include<numeric>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<map>
#define int long long
using namespace std;
const int MaxN = 2e5 + 5;
int ans = 0;
int n;
int a[200001];
int b[200001];
using pii = pair<int, int>;
inline __int128 read()
{
__int128 x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch>'9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void write(__int128 x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
__int128 poww[MaxN];
void solves() {
string a1, b1;
int p;
__int128 temp1 = 0, temp2 = 0, temp = 0, a, b;
cin >> a1 >> b1 >> p;
poww[0] = 1;
for (int i = 1; i <= 100005; i++) {
poww[i] = (poww[i - 1] * 10) % p;
}
for (int i = 0; i < a1.length(); ++i)
{
temp1 = (temp1 + (a1[i] - '0') * poww[a1.length() - i - 1] % p) % p;
}
//cout<<temp1<<endl;
for (int i = 0; i < b1.length(); ++i)
{
temp2 = (temp2 % p + (b1[i] - '0') * poww[b1.length() - i - 1] % p) % p;
}
//cout<<temp2<<endl;
__int128 res = (temp1 * temp2 % p) % p;
write(res);
puts("");
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solves();
return 0;
}