題目傳送門:http://codevs.cn/problem/3990/
題目分析:這是一道CRT入門題。
關於CRT入門我是看MashiroSky大神的blog,個人認爲寫得很好。
然後以下的東西是給自己複習用的,簡記一下,大概寫得很爛QAQ:
現在要求 個形如 的同餘方程組的最小非負整數解 ,其中 兩兩互質。記 ,那麼很明顯 。現在考慮如何讓 滿足第一個方程的條件而不影響其它方程。我們可以算出一個其它所有 的lcm的倍數,即 的倍數,使得它模 剛好等於 。也就是要求:
所以只要算出 在 下的逆元,再乘以 即爲 。用 乘以 即爲要求的lcm的倍數。
然後對於 ~ 也這樣操作,最後將這些數加起來模 ,即爲 。考慮最終的 模每個 的結果,就會發現這樣操作正確性顯然。於是得出式子:
其中 表示x關於y的逆元。
這個式子其實是有很多侷限性的,比如 不能太大。在本題中由於 達到 ,如果寫費馬小定理求逆元要用快速乘。但當某些算法的模數很難處理的時候,可以用CRT將其分解,然後合併。另外,由於符合條件的 在 中僅有唯一取值,還可以將原先難以處理的模數用其它幾個方便處理的互質的模數代換,然後用CRT求出其真實值。比如經典的任意模數NTT,只要選取的模數使 ,即可唯一確定多項式每一位的值。其中 是選取模數之積, 是多項式的次數界, 是原模數。因爲卷積後多項式每一位的值不會超過 。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=15;
typedef long long LL;
LL m[maxn];
LL c[maxn];
LL M;
LL X,Y;
LL a,b;
int n;
void Exgcd(LL u,LL v)
{
if (!v) X=1,Y=0;
else
{
Exgcd(v,u%v);
LL p=X;
LL q=Y;
X=q; //!!!
Y=p-u/v*q; //!!!
}
}
int main()
{
freopen("3990.in","r",stdin);
freopen("3990.out","w",stdout);
scanf("%d%I64d%I64d",&n,&a,&b);
M=1;
for (int i=1; i<=n; i++) scanf("%I64d%I64d",&m[i],&c[i]),M*=m[i];
LL k=0;
for (int i=1; i<=n; i++)
{
Exgcd(m[i],M/m[i]%m[i]);
while (Y<0) Y+=m[i];
k=(k+ M/m[i]*c[i]%M*Y%M )%M;
}
a-=k;
b-=k;
LL num=(b+M)/M-(a-1+M)/M; //!!!
if (!num) printf("0\n0\n");
else
{
a=(a+M-1)/M*M+k;
printf("%I64d\n%I64d\n",num,a);
}
return 0;
}