【浅谈】exgcd 中的整数溢出

首先我们知道, exgcd 中求得 x,y 的绝对值满足 : |x| <=b , |y| <= a 。同时 x, y 在数据大时远小于 a, b 规模。

例一:CF1244C The Football Season

首先看数据范围,a, b<=1e5, p<=1e17 , 来看错误的溢出写法:

x=(x*(p/r)%(b/r)+(b/r))%(b/r)

y=(c-a*x)/b

当 r=1 ,p=1e17 时, x*(p/r) 直接爆 longlong 了。

正确写法:

ll mo=b/r

x=(x%mo*((p/r) % mo)%mo + mo)%mo

y=(c-a*x)/b

这里用到的方法是提前对 p/r 取模。因为 a,b,mo,x <= 1e5 ,所以这里不会溢出。

例二:HDU_4180_RealPhobia

数据范围:a,b <= 2^31

ll mo=b/r;

x=(x%mo*(r%mo)%mo+mo)%mo;

y=(1ll-a*x)/b

前文我们提到 |x| 的规模远小于 a,b 规模,但是真的没有隐患吗?

input: 2147483647 2147483648
output: 2147483647 -2147483646

(取模后的结果)

input: 3147483647 3147483650
output: -1049161217 1049161216

(这是取模前的结果)

可以发现取模前和取模后的结果都不安全。本题中,1-a*x=1-ab>=1-a(a-1)>=-2^62 + 2^31 +1 ,刚好不会爆 longlong 。

摸索一下午得出的结论 : 写一个快速乘除法可以有效避免溢出。

A C AC AC C o d e Code Code

ll fmul(ll x,ll y,ll mod) { x%=mod,y%=mod; if(y<0) y=-y,x=-x; ll sum(0); for(;y;y>>=1) { if(y&1) sum=(sum+x)%mod; x=(x+x)%mod; } return sum; } ll fdiv(ll x,ll y,ll mod) { ll sum(0),tot(0),tmp(x/mod); x%=mod; for(;y;y>>=1) { if(y&1) sum+=tmp,tot+=x; if(tot>=mod) sum++,tot-=mod; x<<=1; if(x>=mod) tmp++,x-=mod; } return sum; } signed main() { while(~scanf("%lld%lld",&a,&b)) { if(gcd(a,b)>1) {printf("sorry\n");continue;} ll x,y,r; exgcd(a,b,x,y,r); if(x>=0) {printf("%lld %lld\n",x,y);continue;} ll mo=b/r; x=(fmul(x,r,mo)+mo)%mo; y=fdiv(1,1,b)-fdiv(a,x,b); //一般地,可以写成 y=fdiv(c,1,d)-fdiv(a,x,d) printf("%lld %lld\n",x,y); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530272.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示