本蒟蒻第一次讲课,由于比较匆忙所以没有及时准备课件,在此表示抱歉…
听说他们讲课都是啥都没有 在那儿唠嗑 20 20 20分钟然后问:有没有不会的 不会的自己学 就完事了?? 2333 2333 2333真是负责
这是一篇数论 0 0 0零起点到负无穷,从入门到入土的博客 请大家做好心理准备
听说 C C C层要讲数论?
哈 作为前数学竞赛生的我当然要抢着讲了 于是跟 p j t pjt pjt大哥蹭了一下
本着放松随便讲一讲的心态 我问了我们这边的 l y s s lyss lyss小宝宝
但是 p j t pjt pjt去自由啊…那就我自己写吧…
t i p s : tips: tips:现在要讲的基本涉及的都是整数 所用的字母除声明外也都表示整数
1.设 a a a、 b b b是给定的数 , b ≠ 0 ,b\neq0 ,b=0。若存在整数 c , c, c,使得 a = b c , a=bc, a=bc,则称 b b b整除 a , a, a,记作 b ∣ a , b\mid a, b∣a,反之 , , ,则称 b b b不能整除 a , a, a,记作 b ∤ a b\nmid a b∤a。
2.一些性质:
a. 若 a ∣ b a|b a∣b,且 b ∣ c b|c b∣c,则 a ∣ c a|c a∣c。
b. 若 b ∣ a b|a b∣a,且 b ∣ c b|c b∣c,则 b ∣ ( a ± c ) b|(a\pm c) b∣(a±c)
c.若 c ∣ a c|a c∣a,且 c ∣ b c|b c∣b,则对于任意整数m、n,有 c ∣ ( m a + n b ) c|(ma+nb) c∣(ma+nb)。
\,\,\,\,\,\,\,\, 对于一个正整数数,如果它有且仅有1和它自己两个约数,那么我们称这个数为素数。如果有两个以上的约数,那么我们称这个数为合数。注意:1既不是素数也不是合数
先植入一个没什么用的定理,它叫素数定理:
小于x的素数的个数近似等于x/ln(x)…
现在我们想想如何求素数
1.考虑暴力枚举
如果 2 − n 2 -\sqrt{n} 2−n每个数都不是 n n n的因子,那么 n n n就是质数了。
复杂度 O ( n ) O(\sqrt{n}) O(n)
那么我们来看一道题:
求 1 − n 1-n 1−n内的素数。 n < = 1000000 n<=1000000 n<=1000000
用上述的算法那是要跑 100 s 100s 100s的 所以我们就需要换一个高效的算法
2.筛法求素数
基本思路是:素数的倍数不是素数
\,\,\,\,\,\,\,\, 1不是素数首先把它筛掉,剩下的数中最小的数一定是素数,然后去掉它的倍数。
v i s [ i ] vis[i] vis[i]表示 i i i是否被访问过 , c n t ,cnt ,cnt表示现在素数的数量 , p r i m e ,prime ,prime数组存的是从小到大的素数
代码:
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
for(int j=1;j*i<=n;j++)vis[i*j]=true;
}
}
这回由 O ( n n ) O(n\sqrt{n}) O(nn)变成了 O ( n l o g n ) O(n\,log_n) O(nlogn)了
但是我们注意到,这样筛会筛重很多次。
我们拿 75 75 75举例 :在筛到素数 3 3 3时我们把它筛除 在筛到素数 5 5 5时我们又会筛除一次 这样会浪费大量的时间,如何优化呢?
3.线性筛
基本思路是:在筛法的基础上 我们让每一个合数只能被他自己最小的素数筛到
如何实现这个优化呢?
看代码
for(int i=2;i<=n;i++){
if(!vis[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
在筛到每个数时 我们把小于它的最小质数的所有质数倍数的数都筛掉 这样就能保证每个数是被它自己最小的质因子筛掉
g c d gcd gcd是啥? l c m lcm lcm是啥?某党?
g c d gcd gcd指的是 g r e a t e s t c o m m o n d i v i s o r greatest\,\,common\,\, divisor greatestcommondivisor就是最大公约数。
l c m lcm lcm指的是 L e a s t C o m m o n M u l t i p l e , Least\,\,Common\,\,Multiple, LeastCommonMultiple,即最小公倍数。
最大公约数是数论中一个重要的概念
设 a a a、 b b b不全为零 , , ,同时整除 a a a、 b b b的整数称为他们的公约数 , , ,显然 a a a、 b b b的公约数只有有限多个 , , ,我们将其中最大的一个称为 a a a、 b b b的最大公约数表示 , , ,用符号 ( a , b ) (a,b) (a,b)表示。显然 , , ,最大公约数是一个正整数。
当 ( a , b ) = 1 (a,b)=1 (a,b)=1时 , , ,我们称 a a a与 b b b互质 ( ( (互素 ) , ), ),这种情形特别重要。
那么问题来了 我们怎么求最大公约数呢?
通常用辗转相除法来写 我的个人喜好是用递归
辗转相除法是不都会…? 算了算了 好好讲讲吧
我们可以很显然地理解这个等式:
g c d ( a , b ) = g c d ( a − b , b ) gcd(a,b)=gcd(a-b,b) gcd(a,b)=gcd(a−b,b)
但是呢 这么一次一次减太慢了 所以我们一次能减多少就减多少
就相当于直接除 这就是简述版的辗转相除
int gcd(int a, int b){
if(b==0)return a;
else return gcd(b,a%b);
我不会告诉你们algorithm库里有可以直接用的__gcd
辗转相除在后面个的扩展 g c d gcd gcd中还是很有用的
上两个题吧
luoguP1372
简述版题意:给你个 n n n和 k k k 求 n n n个数中取 k k k个的最大公约数最大
S o l u t i o n : Solution: Solution:设这 k k k个数的最大公约数为 g c d gcd gcd
则第 k k k个数最小为 k × g c d k×gcd k×gcd 所以 k × g c d ≤ n k×gcd\leq n k×gcd≤n
那么 g c d ≤ n k gcd\leq\frac{n}{k} gcd≤kn 显然最大的 g c d = ⌊ n k ⌋ gcd=\lfloor\frac{n}{k}\rfloor gcd=⌊kn⌋
#include
using namespace std;
int main()
{
int n,k;
cin>>n>>k;
cout<<n/k<<endl;
}
luoguP2090
对于一个数字对(a, b),我们可以通过一次操作将其变为新数字对(a+b, b)或(a, a+b)。
给定一正整数n,问最少需要多少次操作可将数字对(1, 1)变为一个数字对,该数字对至少有一个数字为n。
S o l u t i o n : Solution: Solution:
注意到等式 g c d ( a , b ) = g c d ( a , a + b ) gcd(a,b)=gcd(a,a+b) gcd(a,b)=gcd(a,a+b)
所以找到 1 1 1到 n − 1 n-1 n−1中 g c d ( a , b ) gcd(a,b) gcd(a,b)递归层数最少的就好咯
在这儿推一下那天的模拟赛的解题报告 其中就有这道题 模拟赛不是很难大家可以看看
最后有彩蛋哦
地址在这!:题解
#include
using namespace std;
#define inf int(1e8)
inline void read(int &x){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
x=s*w;
}
int gcdd(int a, int b){
if(!b)return inf;
if(b==1)return a-1;
return gcdd(b,a%b)+a/b;
}
int n,now=inf;
int main(){
read(n);
for(int i=1;i<=(n+1)/2;i++)now=min(now,gcdd(n,i));
printf("%d\n",now);
}
那么我们再简单地谈谈最小公倍数
设 a 、 b a、b a、b是两个非零正整数 , , ,一个同时为 a 、 b a、b a、b的倍数的书称为他们的一个公倍数 , , ,两个数的公倍数显然有无穷多个 , , ,我们把这其中的最小的正整数称为他们的最小公倍数
那最小公倍数就很好求了
l c m ( a , b ) = a b g c d ( a , b ) lcm(a,b)=\frac{ab}{gcd(a,b)} lcm(a,b)=gcd(a,b)ab
U P D : UPD: UPD:昨天讲到这里哦!
经过一 天
同余是数论中的一个重要概念,应用极为广泛。
设 n n n是给定的正整数,若整数 a 、 b a、b a、b满足 n ∣ ( a − b ) , n\mid(a-b), n∣(a−b),则称 a a a和 b b b模 n n n同余 , , ,记作
a ≡ b ( m o d n ) a\equiv b(mod\,\,n) a≡b(modn)
反之,则称 a a a与 b b b模 n n n不同余 , , ,记作
a ≢ b ( m o d n ) a\not\equiv b(mod\,\,n) a≡b(modn)
很显然, a a a 和 b b b模 n n n同余的充分必要条件是 a a a与 b b b被 n n n除得的余数相同。
同余式的几个性质:
给定 a 、 n 、 m a、n、m a、n、m求 a n m o d k a^n\,\,mod\,\,k anmodk
考虑朴素算法 用循环一个一个累乘 可以在 O ( n ) O(n) O(n)的时间内求出答案
不过似乎没什么用 这一点都不快速
那么我们来点快的
考虑到 a 2 n = a n 2 a^{2n}={a^n}^{2} a2n=an2所以我们可以两两配对地乘
所以
1. 1. 1.当n是奇数时,那么有 a n = a ∗ a n − 1 a^{n} = a * a^{n-1} an=a∗an−1
2. 2. 2.当n是偶数时,那么有 a n = a n 2 ∗ a n 2 a^n = a^\frac{n}{2}* a^\frac{n}{2} an=a2n∗a2n
上代码
int quickpow(int a, int n, int m){
int ret=1;
while(b){
if(b&1)(ret*=a)%=m;
(a*=a)%=m,n>>=1;
}
}
时间复杂度: O ( l o g n ) O(log_n) O(logn)
10.23 23 : 12 10.23\,\,\,23:12 10.2323:12今天先写到这儿 明天应该讲不到这儿
引题:给定 a 、 b 、 c , a、b、c, a、b、c,求使得 a x + b y = c ax+by= c ax+by=c成立的最小正整数解 x 、 y , x、y, x、y,如果无解则输出 l y s w a n lyswan lyswan
讲真的 我是从这个题开始认识到信息真的是一门竞赛…
S o l u t i o n : Solution: Solution:
1. 1. 1.首先考虑是否有解:当 g c d ( a , b ) ∣ c gcd(a,b)\mid c gcd(a,b)∣c时方程才有解
2. 2. 2.所以我们先不管 c c c到底是 g c d ( a , b ) gcd(a,b) gcd(a,b)的几倍 直接给
我们考虑如何搞这个方程:
首先看这个方程: b x + ( a % b ) y = c bx+(a\%b)y= c bx+(a%b)y=c这样以此类推
最后会得到 g c d ( a , b ) x = c gcd(a,b)x= c gcd(a,b)x=c此时 x = 1 x=1 x=1
对于 a ′ = b , b ′ = a % b a' = b, b' = a\% b a′=b,b′=a%b而言,我们求得 x , y x, y x,y使得 a ′ x + b ′ y = g c d ( a ′ , b ′ ) , a'x + b'y = gcd(a', b'), a′x+b′y=gcd(a′,b′), 由于 b ′ = a % b = a − ⌊ a b ⌋ × b b' = a \% b = a - \lfloor\frac{a}{b}\rfloor ×b b′=a%b=a−⌊ba⌋×b
所以可以推得 a y + b ( x − ⌊ a b ⌋ × y ) = g c d ( a , b ) ay +b(x - \lfloor\frac{a}{b}\rfloor ×y) = gcd(a, b) ay+b(x−⌊ba⌋×y)=gcd(a,b)
即一组通解为 x = y , y = x − ⌊ a b ⌋ × y x=y,y=x - \lfloor\frac{a}{b}\rfloor ×y x=y,y=x−⌊ba⌋×y
问题来了 我们怎么用代码实现呢?
注意到我们方程的变形用的是 g c d gcd gcd函数辗转相除,方程的通解是一步一步递归才能得到 所以用递归版的欧几里得顺便求得。
这个就叫扩展欧几里得 上代码:
void extended_gcd(int a, int b, int &x, int &y){
if(b==0){x=1,y=0;return;}
exgcd(b,a%b,x,y);
int t=y;
y=x-(a/b)*y,x=t;
}
最后的 x , y x,y x,y就是解 不过出来可能会是一个负数 求最小正整数解还要再加上一个 m o d mod mod
这个东西其实真的很有用的 我们看一道题
N O I P 2012 NOIP2012 NOIP2012同余方程
题意:求关于 x x x的同余方程 a x ≡ 1 ( m o d b ) a x \equiv 1 \pmod {b} ax≡1(modb)的最小正整数解。
S o l u t i o n : Solution: Solution:转化为 a x + b y = 1 ax+by=1 ax+by=1 就变成了扩展欧几里得…
代码:
#include
int t,c,d,x,y;
void exgcd(int a, int b, int &x, int &y)
{
if(a%b==0)
{
x=0;
y=1;
return;
}
exgcd(b,a%b,x,y);
t=x;
x=y;
y=t-a/b*y;
}
int main()
{
scanf("%d%d",&c,&d);
exgcd(c,d,x,y);
while(x<=0)
{
x+=d;
}
printf("%d\n",x);
}
欧拉函数,即 φ ( x ) φ(x) φ(x),简单说它表示从 1 − n 1-n 1−n中和它互质的数的个数
欧拉函数在各种玄学定理中经常出现,有的题中 g c d = 1 gcd=1 gcd=1 的情形往往可以转化为欧拉函数
如何求欧拉函数
1. 1. 1.单个数欧拉函数的求解公式:
对于一个数 x = ∏ i = 1 n p i α i , x=\prod_{i=1}^n p_i^{\alpha_i}, x=∏i=1npiαi,那么 φ ( x ) = x ∏ i = 1 n ( 1 − 1 p i ) φ(x)=x\prod_{i=1}^n (1-\frac{1}{p_i}) φ(x)=x∏i=1n(1−pi1)
至于为啥有兴趣的自己推 不难推,这里就不赘述了。主要是…我现在要码不完了啊啊估计明天也讲不完了所以直接过吧…求饶 . j p g .jpg .jpg
2. 2. 2. 线性求 1 − n 1-n 1−n 所有数的欧拉函数 ( ( (即欧拉筛 ) ) )
上述求解欧拉函数不是没有用 实在是太磨叽…又要分解质因数,又要求乘积,还有分数…没看我代码都没给吗2333 但有时候这个式子推一些性质很有用的
我们欧拉筛之前先要了解几个性质:
a . a. a.对于质数 p p p,有 φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p−1 显然…
b . b. b.当 ( n , m ) = 1 (n,m)=1 (n,m)=1时,有 φ ( n × m ) = φ ( n ) × φ ( m ) ( φ(n×m)=φ(n)×φ(m)( φ(n×m)=φ(n)×φ(m)(即欧拉函数是但不完全是积性函数 ) ) ),特殊地, φ ( 2 n ) = φ ( n ) φ(2n)=φ(n) φ(2n)=φ(n)。
用上面的定义式这个显然成立
c . c. c.当 m ∣ n m\mid n m∣n时, φ ( m × n ) = m × φ ( n ) φ(m×n)=m×φ(n) φ(m×n)=m×φ(n)。
用定义式回去自己推不会问我…
那么这些性质我们了解之后,就可以筛筛筛筛筛筛了
首先想一想 上面的性质是不是有点眼熟? p p p 为质数, n % m = 0 n\%m=0 n%m=0?
没错就是线性筛,我们可以在线性筛素数的时候,运用这三条性质来维护欧拉函数,具体看代码 我要讲不完了!!
euc[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])euc[i]=i-1,prime[++cnt]=i;
for(int j=1;j<=cnt;j++)
{
if(i*prime[j]>n)break;
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
euc[i*prime[j]]=euc[i]*prime[j];
break;
}
else euc[i*prime[j]]=euc[i]*euc[prime[j]];
}
}
仔细体会其实不难
上例题:
题面找不到了 大哥们我错了
题意:给定 n n n,求 1 ≤ x , y ≤ n 1\leq x,y\leq n 1≤x,y≤n且 g c d ( x , y ) = 1 gcd(x,y)=1 gcd(x,y)=1的数对个数
这就用到我们前面讲的 某些问题的 g c d = 1 gcd=1 gcd=1可以转化为欧拉函数
用式子写出来就是:
∑ i = 1 n ∑ j = 1 n ( g c d ( i , j ) = 1 ) \sum_{i=1}^n \sum_{j=1}^n (gcd(i,j)=1) ∑i=1n∑j=1n(gcd(i,j)=1)
= ∑ i = 1 n φ ( i ) =\sum_{i=1}^nφ(i) =∑i=1nφ(i)
所以只需要求出欧拉函数的前缀和即可
终于来到最有用的地方了…
什么是逆元?
对于一个数 x , x, x,它在模 p p p意义下的逆元 a a a,满足 a x ≡ 1 ( m o d p ) , ax\equiv 1\pmod p, ax≡1(modp),
这东西很有用 可以说没有逆元数论少了灵魂
看这个题:给定 n , m , p n,m,p n,m,p求 n m m o d p \frac{n}{m}mod\,\,p mnmodp
如果你没学逆元 你肯定懵逼: w o c ? ? woc?? woc??分数还能取模?
这就是逆元的妙处
S o l u t i o n : Solution: Solution:求出 m m m在模 p p p意义下的逆元 a a a,答案为 n a % p na\%p na%p。
那么这么好用的一个东西 我们怎么求呢???
求逆元的方法
1. 1. 1. e x g c d exgcd exgcd
逆元满足什么条件? a x ≡ 1 ( m o d p ) ax\equiv 1\pmod p ax≡1(modp)?同余方程啊! e x g c d exgcd exgcd显然可行啊。
性能分析:
2. 2. 2.快速幂
快速幂怎么求逆元?
那首先你需要了解一个定理—费马小定理:
若 p p p为素数,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap−1≡1(modp)
这个是怎么来的呢? 欧拉定理的推论
欧拉定理(有时也叫作费马小定理的一般式):
a φ ( p ) ≡ 1 ( m o d p ) a^{φ(p)}\equiv 1\pmod p aφ(p)≡1(modp)
那欧拉定理怎么来的呢?有兴趣自己查查(其实是来不及了!!
接着说 因为 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap−1≡1(modp),所以 a p − 2 a^{p-2} ap−2就是 a a a的逆元
性能分析:
3. 3. 3. 线性求逆元
放心放心 这次线性没有筛 不用害怕
原理: p p p是模数,我们现在要求的是 i i i的逆元
将 p p p写成 p = k ∗ i + r p=k∗i+r p=k∗i+r其中 0 < r < i 0
则 k = p i , r = p % i k=\frac{p}{i},r=p\%i k=ip,r=p%i
解释一下 第二行是第一行两边都除 i r ir ir得到的
总的来说就是一个公式:
i n v [ i ] = − ( m o d / i ) ∗ i n v [ i % m o d ] inv[i]=-(mod/i)*inv[i\%mod] inv[i]=−(mod/i)∗inv[i%mod]边界是 i n v [ 1 ] = 1 inv[1]=1 inv[1]=1
优点:
a . a . a.在 O ( n ) O(n) O(n)的时间内求出 1 − n 1-n 1−n 的所有逆元 高效
b . b. b.代码好打 就一行
c . c. c.没有缺点
代码:
inv[1]=1;
for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[i%mod];
回到前面的问题,现在我们会求逆元了,分数取模自然也就很水了
有理数取余
题意:给出一个有理数 c = a b c=\frac{a}{b} c=ba,求 c m o d 19260817 c\ \bmod 19260817 c mod19260817c的值。
S o l u t i o n : Solution: Solution:直接求 b b b的逆元,由于19260817是个质数,所以输出 a × b m o d − 2 a×b^{mod-2}%mod a×bmod−2就好了,由于 a , b a,b a,b很大 所以需要处理一下:↓
#include
typedef long long ll;
const ll mod=19260817;
ll quickpow(ll a, ll b){
ll ret=1;
while(b){
if(b&1)(ret*=a)%=mod;
(a*=a)%=mod,b>>=1;
}
return ret;
}
ll n,m;
ll getint(){
char chr=getchar();ll x=0;
while(chr<'0'||chr>'9')chr=getchar();
while(chr>='0'&&chr<='9'){
x=(x*10)%mod;
chr=getchar();
}
return x;
}
int main(){
n=getint(),m=getint();
if(m==0)return printf("Angry!"),0;
printf("%lld\n",n*quickpow(m,mod-2)%mod);
}
板子题
题意:给定 n , p n,p n,p求 1 − n 1-n 1−n中所有整数在模 p p p意义下的乘法逆元。
S o l u t i o n : Solution: Solution:没有 S o l u t i o n Solution Solution,线性求逆元直接过
#include
#define N 3000010
typedef long long ll;
using namespace std;
int inv[N],n,p;
inline int read(){
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();};
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch&15);ch=getchar();};
return f*x;
}
int main(){
n=read();p=read();inv[1]=1;puts("1");
for(int i=2;i<=n;i++){
inv[i]=(ll)(p-p/i)*inv[p%i]%p;
printf("%d\n",inv[i]);
}
}
逆元是一种非常有用的工具 它可以和很多有用的定理结合在一起形成扩展定理
中国剩余定理,也称孙子定理,话说昨天你们为什么对这个定理这么感兴趣呢…?
中国剩余定理能干啥呢?
比如 一筐苹果 三个三个吃最后剩两个,四个四个吃最后剩三个,五个五个吃最后剩四个,求一共有多少个苹果?
答案是 59 59 59个.
当然中国剩余定理不只这么简单
给定 k k k以及 k k k个 a i 、 m i a_i、m_i ai、mi求一个最小的正整数 n n n满足
{ n ≡ a 1 ( m o d m 1 ) n ≡ a 2 ( m o d m 2 ) . . . n ≡ a k ( m o d m k ) \begin{cases} n\equiv a_1(\mod m_1)\quad \\ n\equiv a_2(\mod m_2)\quad \\... \\ n\equiv a_k(\mod m_k)\quad \ \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧n≡a1(modm1)n≡a2(modm2)...n≡ak(modmk)
其中 ( m 1 , m 2 , . . . , m k ) = 1 (m_1,m_2,...,m_k)=1 (m1,m2,...,mk)=1
如何求解呢?
令 M = ∏ i = 1 k m i M=\prod_{i=1}^km_i M=∏i=1kmi,即 M M M是所有 m i m_i mi 的最小公倍数
对于每个方程,设 p i = M m i p_i=\frac{M}{m_i} pi=miM
t i t_i ti为同余方程 p i t i ≡ 1 ( m o d m i ) p_it_i\equiv 1(mod\,\,m_i) piti≡1(modmi)的最小非负整数解
则有一个解 x = ∑ i = 1 k a i M m i t i x=\sum_{i=1}^ka_i\frac{M}{m_i}t_i x=∑i=1kaimiMti
通解为 x + i ∗ M ( i ∈ Z ) x+i∗M(i∈Z) x+i∗M(i∈Z)
特别地,最小非负整数解为 ( x % M + M ) % M (x\%M+M)\%M (x%M+M)%M
自己推推 很好理解 盲猜时间应该不够了 所以不讲了 不会问我
代码实现:求 t i t_i ti那个同余方程时要用到扩展欧几里得
其他的…入门难度的代码实现
void ex_gcd(ll n, ll m, ll &x, ll &y){
if(!m){x=1,y=0;return;}
ex_gcd(m,n%m,y,x);y-=(n/m)*x;
}
ll CRT(){
ll ans=0,lcm=1,x,y;
for(int i=1;i<=k;i++)lcm*=b[i];
for(int i=1;i<=k;i++){
t[i]=lcm/b[i];
ex_gcd(t[i],b[i],x,y);
x=(x%b[i]+b[i])%b[i];
ans=(ans+t[i]*a[i]%lcm*x%lcm)%lcm;
}
return (ans+lcm)%lcm;
}
例题例题例题:
T J O I 2009 TJOI2009 TJOI2009猜数字
简述题意:给个 t t t, t t t组数据,每组数据给 k k k以及 k k k个 a i 、 m i a_i、m_i ai、mi求最小的非负整数 n n n,满足对于任意的 i , n − a i i,n - a_i i,n−ai能被 b i b_i bi整除。
S o l u t i o n : Solution: Solution:板子题板子题板子题!!!
从这个:
推到这个:
即:
题中给的 ( b 1 , b 2 , . . . , b k ) = 1 (b_1,b_2,...,b_k)=1 (b1,b2,...,bk)=1所以中国剩余定理可做。
注意:
1. 1. 1.直接乘爆 l o n g l o n g long long longlong要用快速乘,快速乘其实是慢速乘…具体跟快速幂差不多,看代码
2. 2. 2.直接快速乘会 T L E TLE TLE因为快速乘不能有负数,而题中说数可能为负数,所以如果是负数要加个 m o d mod mod…
这两个地方当时差点没坑死我…
代码↓
#include
using namespace std;
typedef long long ll;
inline ll quickpow(ll x, ll y, ll mod){
ll ret=1;
while(y){
if(y&1)(ret*=x)%=mod;
(x*=x)%=mod,y>>=1;
}
return ret;
}
inline ll quickmul(ll x, ll y, ll mod){
if(x<0)x+=mod;if(y<0)y+=mod;
if(y>x)swap(x,y);
ll ret=0;
while(y){
if(y&1)(ret+=x)%=mod;
(x+=x)%=mod,y>>=1;
}
return ret;
}
ll k,a[20],b[20],t[20];
void ex_gcd(ll n, ll m, ll &x, ll &y){
if(!m){x=1,y=0;return;}
ex_gcd(m,n%m,y,x);y-=(n/m)*x;
}
ll CRT(){
ll ans=0,lcm=1,x,y;
for(int i=1;i<=k;i++)lcm*=b[i];
for(int i=1;i<=k;i++){
t[i]=lcm/b[i];
ex_gcd(t[i],b[i],x,y);
x=(x%b[i]+b[i])%b[i];
ans=(ans+quickmul(quickmul(t[i],a[i],lcm),x,lcm))%lcm;
}
return (ans+lcm)%lcm;
}
int main(){
scanf("%lld",&k);
for(int i=1;i<=k;i++)scanf("%lld",&a[i]);
for(int i=1;i<=k;i++)scanf("%lld",&b[i]);
printf("%lld\n",CRT());
}
此外,当模数不是互质时,我们就会用到扩展中国剩余定理了,有兴趣的可以自己学一下
组合数学是信息中的数论的一个重要分支
不过讲不完了
加我 Q Q : 407694747 QQ:407694747 QQ:407694747免费一对一辅导,不容错过…