中国剩余定理有一些细节问题,扩展中国剩余定理的模板适用性最好,但是处理a,b不全为正数的情况会出错,使用的时候要把a,b化为整数,而且,对于一些取模取得较大的数,有可能会爆掉long long ,那么可以有一个很好的解决办法,就是用 他
这个快速乘不仅速度快,而且不会爆掉long long 而且确保输出的数都是正数。
inline ll fmul(ll a,ll b,ll p)
{
return (a*b-(ll)((long double)a*b/p)*p+p)%p;
}
另外普通的中国剩余定理的做法也可能会爆掉long long ,不好处理,
可以这样,很少有毛病,万无一失。
#include<cctype>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<vector>
#define ull unsigned long long
#define ll long long
#define sf(a) scanf("%d",&(a))
#define sff(a) scanf("%lld",&(a))
#define pf(a) printf("%d\n",(a))
#define pff(a) printf("%lld\n",(a))
#define ioss ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define pk push_back
using namespace std;
inline ll fmul(ll a,ll b,ll p)
{
return (a*b-(ll)((long double)a*b/p)*p+p)%p;
}
ll mul(ll a,ll b,ll mod)
{
ll res=0;
while(b>0)
{
if(b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
ll gcd=exgcd(b,a%b,y,x);
y=y-x*(a/b);
return gcd;
}
int n;
ll exchina(ll *a,ll *b)
{
ll m=a[1];
ll ans=b[1];
ll aa,bb,c,gcd,bg;
for(int i=2;i<=n;i++)
{
ll x,y;
aa=m;
bb=a[i];
c=(b[i]-ans%bb+bb)%bb;
gcd=__gcd(aa,bb);
bg=bb/gcd;
exgcd(aa,bb,x,y);
x=fmul(x,c/gcd,bg);
ans=ans+x*m;
m=m*bg;
ans=(ans%m+m)%m;
}
return (ans%m+m)%m;
}
ll a[100005];
ll b[100005];
int main()
{
sf(n);
for(int i=1;i<=n;i++)
{
sff(a[i]);
}
for(int i=1;i<=n;i++)
sff(b[i]);
ll ans=exchina(b,a);
pff(ans);
}
gcd 的性质有很多,又发现了一个,
洛谷p3518
这题推导起来有点麻烦,纯数学的推导是很难做到的,但是,数学相关的定理,引理的推导啥的见的多了可以凭感觉推理,
gcd的这个性质和推导欧拉定理用到的引理,1到p-1对p的逆元属于1到p-1,即使单射也是满射是很像的。有很多小的性质在实际的应用中很重要,因为接近原始数据,又能联系一些公式模板。
有关阶和原根的一些性质,
结合原根与逆元的相关推理和联系还有待完善,弄清楚这些在去看一些相关算法的模板。
最后总结一下今天的小米的比赛,哎,就出了一道题,其实a题和i题没出太可惜了,交了几次后来就泄气了,也是因为因为队友有些急事,没能静下心来好好的弄他,如果能沉下心来好好的看看,还是有很大的希望出的,j题也是这样,错了之后都没有静下心来好好的去看,静下来,不放弃,慢慢来,不太追求速度,才能及解决问题。这一阵子光弄数论了,也得时不时的做做dp的题。
本周的做题截图