浅析中国剩余定理(从CRT到EXCRT))
前置知识
1.
a%b=d,c%b=e,
则(a+c)%b=(d+e)%b(正确性在此不加证明)
2.
a%b=1,则(d\(\times\)a)%b=d%b(正确性在此不加证明)
下面先看一道题(改编自曹冲养猪):
烤绿鸟的故事
题目描述:
mian包是一个贪吃的孩子,这天,他买了一堆绿鸟吃。当然他的妈妈并不想让他吃太多食物(因为那样会发胖),为了避免老妈的唠叨,他决定不告诉他的妈妈绿鸟数量,而是将绿鸟的数量x用以下式子来描述
\]
(\(a_1\),\(a_2\)......\(a_n\)两两互质)
由于他的妈妈数学不好,于是就来向你求助了,请你求出mian包最少买了多少烤绿鸟
输入输出格式
输入格式:
第一行包含一个整数n (n <= 10) – 告诉妈妈的式子数,接下来n行,每行两个整数ai, bi( bi <= ai <= 1000)
首先,我们把这道题简化成下面的图
样例为:
3
3 1
5 1
7 2
很显眼吧?怎么做呢?
1.妈妈,我会暴力
这就很简单了
我们从第二个开始枚举
初始值为a1+b1;
每次自加当前的已经满z足的\(a_1\)**\(a_2\)......\(a_{i-1}\)的乘积(为什么我接下来说)
如果当前值已经满足%\(a_i\)=\(b_i\)
则当前已经成立
ps:上式为什么成立呢?
以上面的例子为例,看下面我的图
当已经满足前两个等式时,我们增加\(a_1\)*\(a_2\)
那么我们会增加15只绿鸟
看图:
很显然,15%3=0,15%5=0
所以增加15的话一定满足当前的等式
自然,暴力就解决了
暴力代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long a,b,c,d,e,f,g,kkk,n;
struct zzzz{
int x,y,z;
}ltt[15];
bool cmp(zzzz s,zzzz t)
{
return s.x>t.x;
}
int main()
{
cin>>n;
kkk=1;
for(a=1;a<=n;a++)
{
cin>>ltt[a].x>>ltt[a].y;
}
sort(ltt+1,ltt+n+1,cmp);
kkk=ltt[1].x;
g=ltt[1].y;
f+=2;
while(f<=n)//枚举每个等式
{
while(1)
{
if(g%ltt[f].x==ltt[f].y)
{
kkk=kkk*ltt[f].x;
f++;
break;
}
g=g+kkk;//自加,直到满足当前等式
}
}
cout<<g;
}
诚然,这种方法好想也好写,但是,我们要注意一个问题
看下面这个式子:
\]
好吧,你自加去吧,保证TLE
这时,我们就需要一个高效的算法
前置知识3:
每一个crt方程组的解在模\(a_1 \times a_2 \times …… \times a_n\)意义下有且只有1个
为什么呢?因为每个解之间的差是所有a的乘积,加数不管对序列中的哪个数取模都是0呗
2.关于暴力的扩展
先说点别的东西
由上面的暴力我们可以看出,该样例crt解法的本质是从5和7的公倍数中找一个除以3余1的数\(n_1\),从3和7的公倍数中找一个除以5余1的数\(n_2\),从3和5的公倍数中找一个除以7余2的数\(n_3\),再将三个数相加得到解。
那么我们可不可以说,更多项的答案也可以这样的得来吗?
答案是肯定的
我们设{\(a_1 \times a_2 \times ...... \times a_n\)}为mul
然后按照暴力的方法,我们可以得到这样一个式子:
\(k_1\times\frac{(mul)}{a1}\)+\(k_2\times\frac{(mul)}{a2}\)+……+\(k_n\times\frac{(mul)}{an}\)
并满足(\(k_i\times\frac{(mul)}{a_i}\)%\(a_i\)=\(b_i\))
正确性?看下面:
首先,因为\(a_i\)两两互质,所以我们可以知道,\(\frac{(mul)}{a_i}\)一定不是\(a_i\)的倍数
而\(\frac{(mul)}{a_j}\)(j\(\ne\)i)一定是\({a_i}\)的倍数
所以,我们知道上式中除了第 \(i\) 项其他所有项 ≡ 0(mod\({a_i}\))
所以,原式 ≡ \(k_i\times\frac{(mul)}{a_i}\)(mod \(a_i\))
然后,我们又根据一开始那个式子的约束条件,得出\(k_i\times\frac{(mul)}{a_i}\)%\(a_i\)=\(b_i\)
所以,整个式子满足对\(a_i\)的约束条件
我们将这个推广开来,即可证得原式是crt方程组的一个解
然后根据前置知识三,即求得原始的最小解
你说了这么多,但该怎么实现呢?
我们设\(\frac{mul}{a_i}\)为\(m_i\)
那么,原式就变成了:
\(k_1 \times m_1 + k_2 \times m_2+......+k_n \times m_n\)
且\(k_i \times m_i\) ≡ \(b_i\) (mod \(a_i\))
** 这是什么?**
很多人表示一脸懵逼,不知道该怎么求???
好,让我们仔细看一下
我们由前置定理2可得:
如果\(k\times m_i\) ≡ 1 (mod \(a_i\))
那么当\(k_i=b_i \times k\)时
\(k_i \times m_i\) ≡ \(b_i\) (mod \(a_i\))
那么怎么求 \(k\) 呢?
我们知道有一个东西叫逆元。。
什么是逆元?
请出门左转以前的一篇日报
这里,我们就可以用逆元来解k咯
代码里我用的是exgcd哦
代码是重点:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rii register int i
#define rij register int j
#define int long long
using namespace std;
int a[15],b[15],n;
void exgcd(int a,int b,int &ls,int &x,int &y)//求逆元
{
if(b==0)
{
ls=a;
x=1;
y=0;
}
else
{
exgcd(b,a%b,ls,y,x);
y-=(a/b)*x;
}
}
signed main()
{
int mod=1,ls,y,x=0;
scanf("%lld",&n);
for(rii=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
for(rii=1;i<=n;i++)
{
mod*=a[i];
}
for(rii=1;i<=n;i++)
{
int kkk=mod/a[i];
exgcd(a[i],kkk,ls,ls,y);
x=(x+y*kkk*b[i])%mod;
}
int ans=(x+mod)%mod;
printf("%lld",ans);
}
毒瘤 Imagine(模板excrt出题人):我的\(a_i\)不互质!
好吧,我们进入下一步,
excrt
难的不是一点半点,好像和上一个已经没什么关系了
因为大家注意一点,上面理论的基础是\(a_i\)互质
那么这里这里不互质了我们怎么办呢?
(说暴力的站墙角)
但我们又没有好的解法......
看来,我们不得不学着暴力两两合并了
当然,我们不能像暴力一样通过自加两两合并
我们要用一个好玩的东西——\(exgcd\)
对于原方程的两个式子,我们可以化成这样(请跟着我的公式):
\]
则\(a_1 \times y+b_1=a_2 \times z+b_2\)
这就是一眼看穿的问题了
所以,这里就能用上面exgcd了
顺次两两合并
每次得出一个当前的解
合并很简单,
我们首先利用exgcd求出一个解
然后利用这个解,新构建一个新的方程带入计算
怎么构建新的解呢?
我们目前有两个方程,一个是由前面的方程合并来的,我们称它为方程1
另一个是我们这一步要合并的方程,称为方程2
\]
我们利用\(exgcd\)可以求出这里的x
现在的关键就在于如何合并\(a\)和\(a_i\)
我们知道,\(x\)+\(lcm\)(\(a\),\(a_i\))仍然满足上面的这个方程组
(因为\(lcm\)(\(a\),\(a_i\))%\(a\)=0,\(lcm\)(\(a\),\(a_i\))%\(a_i\)=0)
我们就可以得出一个新的方程了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rii register int i
#define rij register int j
#define int long long
using namespace std;
int a[100005],b[100005],n,cj,ans,mod;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int gcd=exgcd(b,a%b,x,y);
int zcq=x;
x=y;
y=zcq-a/b*y;
return gcd;
}
int pw(int l,int r,int p)
{
int an=0;
while(r>0)
{
if(r&1)
{
an=(an+l)%p;
}
l*=2;
l%=p;
r/=2;
}
return an;
}
signed main()
{
scanf("%lld",&n);
for(rii=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
cj=a[1];
ans=b[1];
int x,y;
for(rii=2;i<=n;i++)
{
int zcq=cj;
int st=a[i];
int ltt=(b[i]-ans%st+st)%st;
int gys=exgcd(zcq,st,x,y);//求解
int kkk=st/gys;
x=pw(x,ltt/gys,kkk);
ans+=cj*x;
cj*=kkk;
ans+=cj;
ans%=cj;//防溢出(同时确保答案最小)
}
cout<<ans;
}
会了后别忘了写NOI2018屠龙勇士哦~
特别感谢luogu曹冲养猪和excrt题解作者,他们给了我很多思路
如果出锅,请私信联系(评论我不一定看的到),万分感谢
浅析中国剩余定理(从CRT到EXCRT))的更多相关文章
- 欧几里得(辗转相除gcd)、扩欧(exgcd)、中国剩余定理(crt)、扩展中国剩余定理(excrt)简要介绍
1.欧几里得算法(辗转相除法) 直接上gcd和lcm代码. int gcd(int x,int y){ ?x:gcd(y,x%y); } int lcm(int x,int y){ return x* ...
- 中国剩余定理(CRT)及其拓展(ExCRT)
中国剩余定理 CRT 推导 给定\(n\)个同余方程 \[ \left\{ \begin{aligned} x &\equiv a_1 \pmod{m_1} \\ x &\equiv ...
- 中国剩余定理(crt)和扩展中国剩余定理(excrt)
数论守门员二号 =.= 中国剩余定理: 1.一次同余方程组: 一次同余方程组是指形如x≡ai(mod mi) (i=1,2,…,k)的同余方程构成的组 中国剩余定理的主要用途是解一次同余方程组,其中m ...
- 学习笔记:中国剩余定理(CRT)
引入 常想起在空间里见过的一些智力题,这个题你见过吗: 一堆苹果,\(3\)个\(3\)个地取剩\(1\)个,\(5\)个\(5\)个地取剩\(1\)个,\(7\)个\(7\)个地取剩\(2\)个,苹 ...
- 中国剩余定理(CRT)
只看懂了CRT,EXCRT待补.... 心得:记不得这是第几次翻CRT了,每次都有迷迷糊糊的.. 中国剩余定理用来求解类似这样的方程组: 求解的过程中用到了同余方程. x=a1( mod x1) x= ...
- 中国剩余定理(CRT)与欧拉函数[数论]
中国剩余定理 ——!x^n+y^n=z^n 想必大家都听过同余方程这种玩意,但是可能对于中国剩余定理有诸多不解,作为一个MOer&OIer,在此具体说明. 对于同余方程: x≡c1(mod m ...
- (light oj 1319) Monkey Tradition 中国剩余定理(CRT)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1319 In 'MonkeyLand', there is a traditional ...
- 扩展欧几里得(ex_gcd),中国剩余定理(CRT)讲解 有代码
扩展欧几里得算法 求逆元就不说了. ax+by=c 这个怎么求,很好推. 设d=gcd(a,b) 满足d|c方程有解,否则无解. 扩展欧几里得求出来的解是 x是 ax+by=gcd(a,b)的解. 对 ...
- 清北学堂-DAY2-数论专题-中国剩余定理(CRT)
首先请看定义:(百科上抄下来的)孙子定理是中国古代求解一次同余式组(见同余)的方法.是数论中一个重要定理.又称中国余数定理. 一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作&l ...
随机推荐
- LoadRunner对移动互联网后端服务器压力测试
一.LoadRunner简介 LoadRunner,是惠普公司研发的一款预测系统行为和性能的负载测试工具.通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner能够 ...
- 星空灯改装成USB供电
简单的手工活,20分钟搞定 1.用一根USB线剪断,将红黑两根线分别连接到星空灯电源供电的正负极 2.由于USB输出5V 0.5A的电流,因此需要改装下,办法一,加电阻,办法二,换灯泡,由于小电阻并不 ...
- 个人总结-7- 实现图片在MySQL数据库中的存储,取出以及显示在jsp页面上
昨天主要是进行对数据库的内容提取出来并进行动态显示,这个只需要设置一个servlet从数据库中获取数据即可,只是图片比较特殊,不能显示. 今天准备继续找方法来实现图片得录入和显示到jsp中,准备从网上 ...
- zabbix启动报错:Connection to database 'xxx' failed解决方法
Zabbix 分布式系统监视系统 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通 ...
- 【Oracle】DBMS_STATS.GATHER_TABLE_STATS详解
由于Oracle的优化器是CBO,所以对象的统计数据对执行计划的生成至关重要! 作用:DBMS_STATS.GATHER_TABLE_STATS统计表,列,索引的统计信息(默认参数下是对表进行直 ...
- MySQL 8.0.2复制新特性(翻译)
译者:知数堂星耀队 MySQL 8.0.2复制新特性 MySQL 8 正在变得原来越好,而且这也在我们MySQL复制研发团队引起了一阵热潮.我们一直致力于全面提升MySQL复制,通过引入新的和一些有趣 ...
- SQL捕捉blocking信息
场景: 客户抱怨数据库慢,但是回去看的时候,可能已经不慢了,为了查出当时到底是什么原因导致数据慢,制作了下面的存储过程,然后每隔3分钟运行一遍,把blocking信息插入一个数据库中. 主要就是查询s ...
- winform打包发布安装包详解..
winform打包发布安装包详解.. 使用VS 自带的打包工具,制作winform安装项目 开发环境:VS 2008 Access 操作系统:Windows XP 开发语言:C# 项目名称:**管 ...
- python接口测试:自动保存cookies
接口测试中遇到上一个请求返回响应包含cookie(如下图登录请求的响应结果).需将cookies保存下来,后续请求自动带入,否则会提示未登录. python requests的cookie类型是< ...
- API接口数据自检
这个周末的娱乐,通用模块,让后端自检,严格客户端按照文档的要求来,妈妈再也不担心我加班了,对某些团队来说,可能根本用不着,本是想到就尝试一把而已. 哎,傻X的客户端程序员,时间都去推辞扯淡打扮啦,好好 ...