类型:
举个例子,如果建了 3 个猪圈,剩下 1 头猪没有地方安家。如果建造了 5 个猪圈,但是仍然有 1 头猪没有地方去,然后如果建造了 7 个猪圈,还有 2 头没有地方去。求:有多少头猪?
中国剩余定理 (注意:这里的模数两两互质)
然后根据步骤运算就可以了,其中用到了extend_gcd(ps:这个真的好常用啊!)
模板:
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){x=1, y=0; return a;}
LL d, x1, y1;
d = exgcd(b, a%b, x1, y1);
x = y1, y = x1-a/b*y1;
return d;
}
LL CRT(LL m[], LL r[]){
LL M = 1, ans = 0;
for(int i=1;i<=n;i++) M*=m[i];//对应第一步
for(int i=1; i<=n; i++)
{
LL c = M/m[i], x, y;//对应第二步
exgcd(c, m[i], x, y);//对应第三步
ans = (ans+r[i]*c*x%M)%M;//对应第四步
}
return (ans%M + M)%M;//最小正整数解
}
题解:
#include<iostream>
using namespace std;
typedef long long ll;
ll a[10];
ll b[10];
int N;
ll sum = 1;
ll extend_gcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
ll r=extend_gcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return r;
}
}
ll china(ll m[], ll n[])
{
ll ans = 0;
for (int i = 0; i < N; i++)
{
ll t = sum / m[i];
ll x, y;
extend_gcd(t, m[i], x, y);
ans = (ans + n[i] * t * x % sum) % sum;
}
return (ans%sum+sum) % sum;
}
int main()
{
cin >> N;
for (int i = 0; i < N; i++)
{
cin >> a[i] >> b[i];
sum *= a[i];
}
cout << china(a, b) << endl;
return 0;
}
那么,当模数不再两两互质呢?
除此之外:对于 100% 的数据,1≤n≤10^5,1≤bi,ai≤10^12,保证所有 ai 的最小公倍数不超过 10^18;
在这种情况下,由于两两不再互质,那么中国剩余定理的第三步——求ci在mi下的逆元,公式为在这里,由于,又不再互质了,所以gcd最后可能等于0,其逆元自然不存在了。
因此,拓展中国剩余定理应运而出:
所以,过程其实就是进行n-1次方程合并(求p,再通过p求新r,在再求新m)
代码如下:
#include<iostream>
using namespace std;
typedef __int128 ll;
int n;
ll a[100001], b[100001];
ll exdgcd(ll a, ll b, ll& x, ll& y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll t = exdgcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return t;
}
ll CRT(ll* a, ll* b)
{
ll m1 = a[0],r1=b[0];
ll x, y;
for (int i = 1; i < n; i++)
{
ll m2 = a[i], r2 = b[i];
ll tempm = exdgcd(m1, m2, x, y);
x = (x * (r2 - r1) / tempm);
x = (x % (m2 / tempm) + m2 / tempm) % (m2 / tempm);
r1 = (m1 * x + r1);
m1 = (m1 * m2 / tempm);
}
return (r1 % m1 + m1) % m1;
}
int main()
{
scanf("%lld",&n);
for (int i = 0; i < n; i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
printf("%lld",CRT(a,b));
return 0;
}
在这道题我用了__int128,这样拓展到了39位,就不用担心数据溢出了。当然,如果不这用,用高精度也是一样的。
但是他高精度的使用我没有看很懂,因为他是用另两个long long型的数相乘再进行取模,所以每一次进行乘法都要进行取模,有点麻烦:
ll mul(ll a,ll b,ll p)//高精度乘法
{
ll ans=0,x=a;
while(b!=0)
{
if(b&1)//是否为奇数
{
ans=(ans+x)%p;//单独加1个x
}
x=(x+x)%p;//x乘以2
b>>=1;//除以二
}
return (ans%p+p)%p;
}