知识点 - 带模运算 快速模乘 各种情况模除

知识点 - 带模运算 快速模乘 各种情况模除

解决问题类型:

加法取模

一般来说, 把 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

  1. 在计算乘法时,如果 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)
  2. 如果需要更快的快速乘法,可以使用 long double 数据类型进行计算,复杂度为 O ( 1 ) O(1) O(1)
  3. 上面的都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

  1. 在计算除法时,如果模数是素数,则求逆元 b − 1 = b c − 2 b^{-1}=b^{c-2} b1=bc2(费马小定理)

  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 ba ,ba%c=ba%(bc)

  3. 在计算除法时,如果模数不是素数,b不整除a,但b和c互素,则用扩展欧几里得求逆元。

  4. 在计算除法时,如果模数不是素数,b不整除a,且b和c不互素,但b是c的倍数,可以将b中所有c的因子约去, 变成情况2,用扩展欧几里得求逆元。

  5. 在计算除法时,如果模数不是素数,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;}
};
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值