链接:https://ac.nowcoder.com/acm/contest/392/B
来源:牛客网
题目描述
找到了心仪的小姐姐月月后,华华很高兴的和她聊着天。然而月月的作业很多,不能继续陪华华聊天了。华华为了尽快和月月继续聊天,就提出帮她做一部分作业。
月月的其中一项作业是:给定正整数A、B、P,求ABmodPABmodP的值。华华觉得这实在是毫无意义,所以决定写一个程序来做。但是华华并不会写程序,所以这个任务就交给你了。
因为月月的作业很多,所以有T组询问。
月月的其中一项作业是:给定正整数A、B、P,求ABmodPABmodP的值。华华觉得这实在是毫无意义,所以决定写一个程序来做。但是华华并不会写程序,所以这个任务就交给你了。
因为月月的作业很多,所以有T组询问。
输入描述:
第一行一个正整数T表示测试数据组数。
接下来T行,每行三个正整数A、B、P,含义如上文。
输出描述:
输出T行,每行一个非负整数表示答案。
备注:
1≤T≤1031≤T≤103,1≤A,B,P≤1018
方法一 直接用大数模板加快速幂计算
#include <iostream> #include <cstring> #include <cstdio> #include <string> #include <queue> #include <list> #include <map> #include <set> #include <cmath> #include <bitset> #include <vector> #include <sstream> #include <cstdlib> #include <algorithm> using namespace std; typedef long long ll; #define mem(a, x) memset(a, x, sizeof a) #define foreach(e,x) for(__typeof(x.begin()) e=x.begin();e!=x.end();++e) #define fori(i,l,u) for(ll (i)=(ll)(l);(i)<=(ll)(u);++(i)) #define ford(i,l,u) for(ll (i)=(ll)(l);(i)>=(ll)(u);--(i)) ll powermod(ll a, ll exp, ll moder){ ll ret = 1; for ( ; exp; exp >>= 1){ if (exp & 1){ ret = 1ll * ret * a % moder; } a = 1ll * a * a % moder; } return ret; } void addminus(int *a, int *b, int &lengtha, int &lengthb, int type){ int length = std::max(lengtha, lengthb); for (int i = 0; i < length; ++ i){ a[i] += type == 1 ? b[i] : -b[i]; a[i] >= 10 ? (a[i] -= 10, ++ a[i + 1]) : a[i] < 0 ? (a[i] += 10, -- a[i + 1]) : 0; } for (lengtha = length + 1; lengtha && !a[lengtha - 1]; -- lengtha); } struct biginteger{ // 3y·¨oí?a?ùdèòa??±????? const static int max = 6; //2^max位数字 const static int moder = (119 << 23) + 1; const static int root = 3; const static int invroot = 332748118; int a[1 << max]; int length, sig; biginteger(){ memset(a, 0, sizeof(a)); length = sig = 0; } void clear(){ memset(a, 0, sizeof(int) * length); length = sig = 0; } void read(){ clear(); char ch = getchar(); for ( ; (ch < '0' || ch > '9') && ch != '-'; ch = getchar()) ; ch == '-' ? (sig = -1, ch = getchar()) : sig = 1; for ( ; ch >= '0' && ch <= '9'; ch = getchar()){ a[length ++] = ch - '0'; } std::reverse(a, a + length); for ( ; length && !a[length - 1]; -- length) ; sig = length ? sig : 0; } void print(){ if (!sig) return (void)putchar('0'); if (sig < 0){ putchar('-'); } for (int i = length - 1; i >= 0; i--){ putchar(a[i] + '0'); } } template <typename t> t tointeger(){ t ret = 0; for (int i = length - 1; i >= 0; ++ i){ ret = ret * 10 + a[i]; } return ret * sig; } ll tointeger(){ ll ret = 0; for (int i = length - 1; i >= 0; ++ i){ ret = ret * 10 + a[i]; } return ret * sig; } bool equal(const biginteger &p) const &{ if (sig != p.sig || length != p.length) return false; for (int i = 0; i < length; ++ i){ if (a[i] != p.a[i]) return false; } return true; } bool greater(const biginteger &p) const &{ if (sig != p.sig) return sig > p.sig; if (length != p.length) return length > p.length ^ sig == -1; for (int i = length - 1; i >= 0; -- i){ if (a[i] > p.a[i]) return sig > 0; else if (a[i] < p.a[i]) return sig < 0; } return false; } void leftshift(int dis){ for (int i = length + dis - 1; i >= dis; -- i){ a[i] = a[i - dis]; } memset(a, 0, sizeof(int) * dis); length += dis; } void rightshift(int dis){ if (dis >= length) return clear(); for (int i = 0; i < length - dis; ++ i){ a[i] = a[i + dis]; } memset(a + length - dis, 0, sizeof(int) * dis); length = length - dis > 0 ? length - dis : 0; } void addone(){ sig >= 0 ? ++ a[0] : -- a[0]; for (int i = 0; i < length; ++ i){ if (a[i] < 10 && a[i] >= 0) break; a[i] >= 10 ? (a[i] -= 10, ++ a[i + 1]) : (a[i] += 10, -- a[i + 1]); } if (a[length]){ ++ length; } if (!a[length - 1]){ -- length; } sig = length ? (sig >= 0 ? 1 : -1) : 0; } void minusone(){ sig = -sig; addone(); sig = -sig; } bool absgreaterequal(biginteger &q){ if (length != q.length) return length > q.length; for (int i = length - 1; i >= 0; -- i){ if (a[i] > q.a[i]) return true; if (a[i] < q.a[i]) return false; } return true; } void abs(){ sig = std::abs(sig); } void neg(){ sig = -sig; } void assign(biginteger &q){ memset(a, 0, sizeof(int) * length); memcpy(a, q.a, sizeof(int) * q.length); length = q.length; sig = q.sig; } void assign2(ll q){ memset(a, 0, sizeof(int) * length); if (!q) return (void) (sig = length = 0); q < 0 ? sig = -1, q = -q : sig = 1; length = 0; for ( ; q; q /= 10){ a[length ++] = q % 10; } //cout<<"length: "<<length<<endl; } template <typename t> void assign(t q){ memset(a, 0, sizeof(int) * length); if (!q) return (void) (sig = length = 0); q < 0 ? sig = -1, q = -q : sig = 1; length = 0; for ( ; q; q /= 10){ a[length ++] = q % 10; } } void add(biginteger &q){ static biginteger aux; if (!q.sig) return; if (!sig){ assign(q); return; } if (sig == q.sig){ addminus(a, q.a, length, q.length, 1); return; } if (absgreaterequal(q)){ addminus(a, q.a, length, q.length, -1); sig = length ? sig : 0; return; } aux.assign(q); addminus(q.a, a, q.length, length, -1); assign(q); q.assign(aux); } void minus(biginteger &q){ q.neg(); add(q); q.neg(); } void ntt(int *a, int length, int type){ int len = -1; for (int x = length; x; ++ len, x >>= 1); for(int i = 1, j = 0; i < length - 1; ++ i){ for(int s = length; j ^= s >>= 1, ~j & s; ) ; if(i < j){ std::swap(a[i], a[j]); } } for (int i = 1; i <= len; ++ i){ for (int j = 0, unit = powermod(type == 1 ? root : invroot, moder - 1 >> i, moder), szk = 1 << i - 1; j < length; j += 1 << i){ for (int k = j, w = 1; k < j + szk; ++ k){ int s = a[k], t = 1ll * w * a[k + szk] % moder; a[k] = s + t >= moder ? s + t - moder : s + t; a[k + szk] = s - t < 0 ? s - t + moder : s - t; w = 1ll * w * unit % moder; } } } if (type == 1) return; int inv = powermod(length, moder - 2, moder); for (int i = 0; i < length; ++ i){ a[i] = 1ll * a[i] * inv % moder; } } void mult(biginteger &q){ static int aux[1 << max]; if (!sig || !q.sig) return clear(); int n = length + q.length; int lengthans = 1; for ( ; lengthans < n; lengthans <<= 1) ; memcpy(aux, q.a, sizeof(int) * lengthans); ntt(a, lengthans, 1); ntt(aux, lengthans, 1); for (int i = 0; i < lengthans; i++){ a[i] = 1ll * a[i] * aux[i] % moder; } ntt(a, lengthans, -1); for (int i = 0; i < n - 1; i++){ a[i + 1] += a[i] / 10; a[i] %= 10; } length = n; for ( ; length && !a[length - 1]; -- length) ; sig *= q.sig; } void mult(ll q){ if (!q || !sig) return clear(); ll x = std::abs(q), remain = 0; for (ll i = 0; i < length; ++ i){ remain += a[i] * x; a[i] = remain % 10; remain /= 10; } a[length] = remain; for ( ; a[length]; ++ length){ a[length + 1] = a[length] / 10; a[length] %= 10; } for ( ; length && !a[length - 1]; -- length) ; sig *= q < 0 ? -1 : 1; } // ??·?ê±power±è?±?ó3??y void power(int exp){ static biginteger aux; if (!sig) return; aux.assign <int>(1); for ( ; exp; exp >>= 1){ if (exp & 1){ aux.mult(*this); } aux.mult(aux); } assign(aux); } void divide(biginteger &q){ static biginteger aux, aux1; if (!sig || !q.sig) return; if (length < q.length) return clear(); bool neg1 = sig == 1, neg2 = q.sig == 1; abs(), q.abs(); int num = 0; for (int i = q.length - 1; i >= q.length - 3; -- i){ (num *= 10) += i >= 0 ? q.a[i] : 0; } num = 100000 / num; int nowprecision = 1; aux.assign <int>(num); for ( ; nowprecision <= length - q.length; nowprecision <<= 1){ aux1.clear(); aux1.length = (nowprecision << 1) + 3, aux1.sig = 1; for (int i = q.length - aux1.length; i < q.length; ++ i){ aux1.a[i - q.length + aux1.length] = i >= 0 ? q.a[i] : 0; } aux1.mult(aux), aux1.rightshift(nowprecision + 2); aux1.mult(aux), aux1.rightshift(nowprecision + 2); aux.mult(2); aux.leftshift(nowprecision); aux.minus(aux1); } aux.mult(*this); aux.rightshift(q.length + nowprecision + 1); aux1.assign(aux); aux1.mult(q); minus(aux1); int flag = absgreaterequal(q) ? 2 : sig < 0 ? 1 : 0; assign(aux); if (flag){ flag == 1 ? minusone() : addone(); } if (!neg2){ q.neg(); } sig *= neg1 ^ neg2 ? -1 : 1; } ll divide(ll q){ if (!sig || !q) return 0; ll remain = 0, x = std::abs(q); for (int i = length - 1; i >= 0; -- i){ remain = remain * 10 + a[i]; a[i] = remain / x; remain %= x; } for ( ; length && !a[length - 1]; -- length); remain *= sig; sig *= q < 0 ? -1 : 1; if (!length){ sig = 0; } return remain; } void sqrt(){ static biginteger aux, aux1, aux2; if (sig <= 0) return; int num = 0; for (int i = length - 1; i >= length - 8; -- i){ (num *= 10) += i >= 0 ? a[i] : 0; } ll x = length & 1 ? 10000000000000ll : 100000000000000ll; num = std::sqrt(1.0 * x / num); int nowprecision = 2; aux.assign <int>(num); for ( ; nowprecision <= (length >> 1) + 1; nowprecision = (nowprecision << 1) - 1){ aux1.clear(), aux2.clear(); aux1.length = (nowprecision << 1) + 1 + (length & 1), aux1.sig = 1; for (int i = length - aux1.length; i < length; ++ i){ aux1.a[i - length + aux1.length] = i >= 0 ? a[i] : 0; } aux1.mult(aux), aux1.rightshift(nowprecision + 1); aux1.mult(aux), aux1.rightshift(nowprecision + 1); aux1.divide(2); aux2.length = nowprecision + 1 << 1, aux2.sig = 1; aux2.a[aux2.length - 1] = 1, aux2.a[aux2.length - 2] = 5; aux2.minus(aux1); aux.mult(aux2); aux.rightshift(nowprecision + 2); } aux.mult(*this); aux.rightshift((length >> 1) + nowprecision + 1); aux1.assign(aux); aux1.mult(aux1); aux2.assign(*this); aux2.mult(2); minus(aux1); int flag = greater(aux2) ? 2 : sig < 0 ? 1 : 0; assign(aux); if (flag){ flag == 1 ? minusone() : addone(); } } }; ll f(ll a,ll b,ll c){ biginteger temp1,temp2; temp1.assign2(a); temp2.assign2(b); //temp.print(); temp1.mult(temp2); a=temp1.divide(c); //cout<<"a=="<<a<<endl; //if(a<0) { cout<<"a="<<a<<" a<0"<<endl;a+=c; } // a = temp.tointeger(); return a; } ll powermod2(ll a, ll exp, ll moder){ a=a%moder; ll ret = 1; for ( ; exp!=0; exp >>= 1){ if (exp & 1){ //ret = 1ll * ret * a % moder; ret=f(a,ret,moder); } a=f(a,a,moder); //a = 1ll * a * a % moder; } return ret; } int T; int main(){ ios::sync_with_stdio(false); //freopen("local.in","r",stdin); // biginteger temp; // temp.assign2(9); // //temp.print(); // temp.mult(7); // int t=temp.divide(5); // cout<<"temp: "; // temp.print(); // cout<<endl; // cout<<"a: "<<t<<endl; while(cin>>T){ fori(i,1,T){ ll a,b,c; cin>>a>>b>>c; cout<<powermod2(a,b,c)<<endl; } } // c.assign(a); // a.divide(b); // a.print(); // putchar(' '); // a.mult(b); // c.minus(a); // c.print(); // putchar(' '); return 0; }
方法二 快速乘解决中间的溢出问题
快速乘和快速幂类似,因为a*b%c可以理解为b个a相加后取模,即b次加法运算。对于加法运算,将b次加法按照b的二进制表示进行log(b)级别的加法计算是等价的。
例如11次加法,可以拆成1+2+0+8次加法运算,因此可以用类似快速幂的思想计算。 参考博客:https://blog.csdn.net/m0_37579232/article/details/88382947
#include <iostream> #include <cstring> #include <cstdio> #include <string> #include <queue> #include <list> #include <map> #include <set> #include <cmath> #include <bitset> #include <vector> #include <sstream> #include <cstdlib> #include <algorithm> using namespace std; typedef long long ll; #define mem(a, x) memset(a, x, sizeof a) #define foreach(e,x) for(__typeof(x.begin()) e=x.begin();e!=x.end();++e) #define fori(i,l,u) for(ll (i)=(ll)(l);(i)<=(ll)(u);++(i)) #define ford(i,l,u) for(ll (i)=(ll)(l);(i)>=(ll)(u);--(i)) ll f(ll a,ll b,ll p){//a*b%p ll res=0; while(b){ if(b&1)res=(res+a)%p; b>>=1; a=(a+a)%p; } return res; } ll powermod2(ll a, ll exp, ll moder){ a=a%moder; ll ret = 1; for ( ; exp!=0; exp >>= 1){ if (exp & 1){ //ret = 1ll * ret * a % moder; ret=f(a,ret,moder); } a=f(a,a,moder); //a = 1ll * a * a % moder; } return ret; } int T; int main(){ ios::sync_with_stdio(false); //freopen("local.in","r",stdin); // biginteger temp; // temp.assign2(9); // //temp.print(); // temp.mult(7); // int t=temp.divide(5); // cout<<"temp: "; // temp.print(); // cout<<endl; // cout<<"a: "<<t<<endl; while(cin>>T){ fori(i,1,T){ ll a,b,c; cin>>a>>b>>c; cout<<powermod2(a,b,c)<<endl; } } // c.assign(a); // a.divide(b); // a.print(); // putchar(' '); // a.mult(b); // c.minus(a); // c.print(); // putchar(' '); return 0; }
方法三 平台支持不稳定的 __int128类型
如果可以用这种类型的话可以直接替换爆精度的位置就可以。
这种数据类型的使用注意事项: