知识点 - 带模运算 快速模乘 各种情况模除
解决问题类型:
加法取模
一般来说, 把 x 加上 v 的取模 写法就是 ((x+v)%MOD+MOD)%MOD
。
但如果已知 x 和 v 都是非负,那么把它改写为x+=v;if(x>=MOD)x-=MOD;
速度将会快两倍以上。
同理,若v是负的则要写成 x+=v;if(x<0)x+=MOD。
快速模乘
( a b ) % c = ( a % c ) ( b % c ) % c (ab)\%c=(a\%c)(b\%c)\%c (ab)%c=(a%c)(b%c)%c
- 在计算乘法时,如果 c c c 较大(但不超过64位整数范围)导致 ( a % c ) ( b % c ) (a\%c)(b\%c) (a%c)(b%c)爆long long ,可以使用快速幂乘法进行计算,原理与快速幂运算类似,复杂度为 O ( log b ) O(\log b) O(logb)
- 如果需要更快的快速乘法,可以使用 long double 数据类型进行计算,复杂度为 O ( 1 ) O(1) O(1)
- 上面的都T了,用蒙哥马利快速乘(Montgomery Multiplication),优化mod时间花费
各种情况模除
a b % c ! = a % c b % c % c \frac{a}{b}\%c\ != \frac{a\%c}{b\%c}\%c ba%c !=b%ca%c%c
-
在计算除法时,如果模数是素数,则求逆元 b − 1 = b c − 2 b^{-1}=b^{c-2} b−1=bc−2(费马小定理)
-
在计算除法时,如果模数不是素数,且b整除a,可以用下面的方法。
i f b ∣ a , a b % c = a % ( b c ) b if\ b|a\ , \frac{a}{b}\%c=\frac{a\%(bc)}{b} if b∣a ,ba%c=ba%(bc) -
在计算除法时,如果模数不是素数,b不整除a,但b和c互素,则用扩展欧几里得求逆元。
-
在计算除法时,如果模数不是素数,b不整除a,且b和c不互素,但b是c的倍数,可以将b中所有c的因子约去, 变成情况2,用扩展欧几里得求逆元。
-
在计算除法时,如果模数不是素数,b不整除a,且b和c不互素,且b不是c的倍数,只能用高精度。
复杂度:
例题
代码
//二次幂快速模乘
ll fastMul(ll a,ll b,ll p){
a%=p;
ll ans=0;
while(b>0){
if(b&1)ans=(ans+a)%p;
b>>=1;
a=(a+a)%p;
}
return ans;
}
//long double底层优化模乘
ll modMul(ll a,ll b,ll p){
if (p<=1000000000) return a*b%p;
else if (p<=1000000000000ll) return (((a*(b>>20)%p)<<20)+(a*(b&((1<<20)-1))))%p;
else {
ll d=(ll)floor(a*(long double)b/p+0.5);
ll ret=(a*b-d*p)%p;
if (ret<0) ret+=p;
return ret;
}
}
//蒙哥马利模乘 自定义int256, 毛子版
using u64 = uint64_t;
using u128 = __uint128_t;
using i128 = __int128_t;
struct u256 {
u128 high, low;
static u256 mult(u128 x, u128 y) {
u64 a = x >> 64, b = x;
u64 c = y >> 64, d = y;
// (a*2^64 + b) * (c*2^64 + d) =
// (a*c) * 2^128 + (a*d + b*c)*2^64 + (b*d)
u128 ac = (u128)a * c;
u128 ad = (u128)a * d;
u128 bc = (u128)b * c;
u128 bd = (u128)b * d;
u128 carry = (u128)(u64)ad + (u128)(u64)bc + (bd >> 64u);
u128 high = ac + (ad >> 64u) + (bc >> 64u) + (carry >> 64u);
u128 low = (ad << 64u) + (bc << 64u) + bd;
return {high, low};
}
};
struct Montgomery {
Montgomery(u128 n) : mod(n), inv(1) {
for (int i = 0; i < 7; i++)
inv *= 2 - n * inv;
}
u128 init(u128 x) {
x %= mod;
for (int i = 0; i < 128; i++) {
x <<= 1;
if (x >= mod)
x -= mod;
}
return x;
}
u128 reduce(u256 x) {
u128 q = x.low * inv;
i128 a = x.high - u256::mult(q, mod).high;
if (a < 0)
a += mod;
return a;
}
u128 mult(u128 a, u128 b) {
return reduce(u256::mult(a, b));
}
u128 mod, inv;
};
//加速转换
struct Montgomery {
Montgomery(u128 n) : mod(n), inv(1), r2(-n % n) {
for (int i = 0; i < 7; i++)
inv *= 2 - n * inv;
for (int i = 0; i < 4; i++) {
r2 <<= 1;
if (r2 >= mod)
r2 -= mod;
}
for (int i = 0; i < 5; i++)
r2 = mul(r2, r2);
}
u128 init(u128 x) {
return mult(x, r2);
}
u128 mod, inv, r2;
};
//dls版子
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head
typedef unsigned long long u64;
typedef __int128_t i128;
typedef __uint128_t u128;
int _,k;
u64 A0,A1,M0,M1,C,M;
struct Mod64 {
Mod64():n_(0) {}
Mod64(u64 n):n_(init(n)) {}
static u64 init(u64 w) { return reduce(u128(w) * r2); }
static void set_mod(u64 m) {
mod=m; assert(mod&1);
inv=m; rep(i,0,5) inv*=2-inv*m;
r2=-u128(m)%m;
}
static u64 reduce(u128 x) {
u64 y=u64(x>>64)-u64((u128(u64(x)*inv)*mod)>>64);
return ll(y)<0?y+mod:y;
}
Mod64& operator += (Mod64 rhs) { n_+=rhs.n_-mod; if (ll(n_)<0) n_+=mod; return *this; }
Mod64 operator + (Mod64 rhs) const { return Mod64(*this)+=rhs; }
Mod64& operator -= (Mod64 rhs) { n_-=rhs.n_; if (ll(n_)<0) n_+=mod; return *this; }
Mod64 operator - (Mod64 rhs) const { return Mod64(*this)-=rhs; }
Mod64& operator *= (Mod64 rhs) { n_=reduce(u128(n_)*rhs.n_); return *this; }
Mod64 operator * (Mod64 rhs) const { return Mod64(*this)*=rhs; }
u64 get() const { return reduce(n_); }
static u64 mod,inv,r2;
u64 n_;
};
u64 Mod64::mod,Mod64::inv,Mod64::r2;
u64 pmod(u64 a,u64 b,u64 p) {
u64 d=(u64)floor(a*(long double)b/p+0.5);
ll ret=a*b-d*p;
if (ret<0) ret+=p;
return ret;
}
void bruteforce() {
u64 ans=1;
for (int i=0;i<=k;i++) {
ans=pmod(ans,A0,M);
u64 A2=pmod(M0,A1,M)+pmod(M1,A0,M)+C;
while (A2>=M) A2-=M;
A0=A1; A1=A2;
}
printf("%llu\n",ans);
}
int main() {
for (scanf("%d",&_);_;_--) {
scanf("%llu%llu%llu%llu%llu%llu%d",&A0,&A1,&M0,&M1,&C,&M,&k);
Mod64::set_mod(M);
Mod64 a0(A0),a1(A1),m0(M0),m1(M1),c(C),ans(1),a2(0);
for (int i=0;i<=k;i++) {
ans=ans*a0;
a2=m0*a1+m1*a0+c;
a0=a1; a1=a2;
}
printf("%llu\n",ans.get());
}
}
整环 ( Z p , + , ⋅ ) (\mathbb{Z}_p,+,\cdot) (Zp,+,⋅) 中的数写成这样一个类:
class mint{
static const ll mo=1e9+7;
inline ll fpow(ll a,ll b){ll c=1;while(b){if(b&1)c=c*a%mo;b>>=1;a=a*a%mo;}return c;}
inline ll inv(ll a){return fpow(a,mo-2);}
inline ll norm(ll a){return a<0?(a%mo+mo):a%mo;}
public:
ll v;
mint(){}
mint(ll x):v(x){}
mint &operator =(const ll b){v=norm(b);return *this;}
mint &operator +=(const mint &b){v+=b.v;if(v>=mo)v-=mo;return *this;}
mint &operator -=(const mint &b){v-=b.v;if(v<0)v+=mo;return *this;}
mint &operator *=(const mint &b){v=v*b.v%mo;return *this;}
mint &operator /=(const mint &b){v=v*inv(b.v)%mo;return *this;}
mint operator + (const mint &b)const{mint a{*this};return a+=b;}
mint operator - (const mint &b)const{mint a{*this};return a-=b;}
mint operator * (const mint &b)const{mint a{*this};return a*=b;}
mint operator / (const mint &b)const{mint a{*this};return a/=b;}
mint operator ^ (const mint &b){return mint(fpow(v,b.v));}
mint &operator++(){v++;if(v==mo)v-=mo;return *this;}
mint &operator--(){v--;if(v==-1)v+=mo;return *this;}
mint operator -()const{return mint(v?(mo-v):0);}
bool operator ==(const mint &b)const{return v==b.v;}
bool operator <(const mint &b)const{return v<b.v;}
bool operator >(const mint &b)const{return v<b.v;}
bool operator !=(const mint &b)const{return v!=b.v;}
explicit operator ll()const{return v;}
friend ostream &operator << (ostream &o,const mint & b){o<<b.v;return o;}
friend istream &operator >> (istream &in,mint &a){in>>a.v;a.v%=mo;return in;}
};