基础训练赛第七场--26104--F题(快速幂,快速乘法,快读,_int128,预处理)


题解思路:

 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;
}

咱就是说this->F题题源


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值