\(crt,Chinese\ Remainder\ Theorem\)

概述

  • 前置技能:同余基础性质,\(exgcd\).
  • \(crt\),中国剩余定理.用于解决模数互质的线性同余方程组.大概长这样:

\[\begin{equation}
\left\{
\begin{array}{lr}
x\equiv a_1(mod\ m_1),\\
x\equiv a_2(mod\ m_2),\\
x\equiv a_3(mod\ m_3),\\
......\\
x\equiv a_n(mod\ m_n).\\
\end{array}
\right.
\end{equation}
\]

  • 其中,所有的 \(m\) 两两互质,否则需要使用 \(excrt\),此处不讨论.
  • 记\(N=\prod m_i\),对于每个方程\(x\equiv a_i(mod\ m_i)\),尝试找一个 \(y_i\) 满足\((N/m_i)*y_i\equiv 1(mod\ m_i).\)
  • 这里的\(N/m_i\)就是其他所有模数的积,因为所有 \(m\) 互质,所以\(N/m_i\)与\(m_i\)互质.那么上面的线性同余方程一定可以通过 \(exgcd\) 找到这样的 \(y_i\).
  • 再记\(x_i=(N/m_i)*y_i\),那么\(x_i*a_i\equiv a_i(mod\ m_i),x_i*a_i\equiv 0(mod\ m_j,\forall j\not= i)\).
  • 将各个方程合并起来即可得出原方程组的解\(x=\sum x_ia_i\),容易验证,这个构造出的 \(x\) 满足原方程组,且对于\(\forall x'\equiv x(mod\ N),x'\)也是合法的解.
  • 时间复杂度为\(O(n\cdot(log\prod m_i))\).

应用

  • 解线性同余方程组,拆分处理,合并答案.

题目

曹冲养猪

  • 题意:求解一个线性同余方程组的最小正整数解,其中模数互质.
  • 直接使用 \(crt\) 求解.因为要求最小正整数解,所以在合并的时候对 \(N\) 取模,且最后确保为正.
#include"bits/stdc++.h"
#define int long long
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
const int MAXN=11;
int a[MAXN],m[MAXN];
int n;
void exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;
y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
}
int crt(int a[],int m[],int n)
{
int res=0,N=1,x,y;
for(int i=1;i<=n;++i)
N*=m[i];
for(int i=1;i<=n;++i)
{
int tmp=N/m[i];
exgcd(m[i],tmp,x,y);
res=(res+y*tmp*a[i])%N;
}
return (res+N)%N;
}
signed main()
{
n=read();
for(int i=1;i<=n;++i)
m[i]=read(),a[i]=read();
int ans=crt(a,m,n);
printf("%lld\n",ans);
return 0;
}

\(Excrt,Extended\ crt\)

概述

  • 前置技能:\(crt\)(其实不是很必要),\(exgcd\).
  • 在 \(crt\) 中,要求所有模数互质.如果模数不互质, \(crt\) 就解决不了问题.因为构造过程中会出现\(N/m_i\)与\(m_i\)不互质的情况,同余方程\((N/m_i)*y_i\equiv 1(mod\ m_i)\)会出现无解的情况.
  • 这时候就需要使用 \(excrt\) 进行处理.
  • \(excrt\) 的基本思路与 \(crt\) 不同,\(crt\) 是直接通过构造得出解, \(excrt\) 是通过将方程两两合并,直到最后只剩下一个方程,也就得到了通解.
  • 对于如下方程组:

\[\begin{equation}\left\{ \begin{array}{lr} x\equiv a_1(mod\ m_1),\\ x\equiv a_2(mod\ m_2),\\ x\equiv a_3(mod\ m_3),\\ ......\\ x\equiv a_n(mod\ m_n).\\ \end{array}\right.\end{equation}
\]

  • 先考虑合并前两个方程,将其写成一个等价的线性同余方程.
  • 由同余的基本性质可知,一个合法解 \(x_0\) 满足 \(x_0=a_1+k_1*m_1=a_2+k_2*m_2.\)
  • 将后两个式子移项整理可得\(k_2*m_2-k_1*m_1=a_1-a_2.\)
  • 这里就是线性不定方程的形式了,尝试使用 \(exgcd\) 求解.
  • 可记\(gcd(m_1,m_2)=g,a_1-a_2=c.\)那么当\(g\not|\ c\)时,显然无解,这两个方程无法同时满足,那么整个方程组也就无解.
  • 否则,先通过 \(exgcd\) 求出满足方程 \(k_2*m_2-k_1*m_1=g\)的一组解\(k_2',-k_1'\).
  • 方程两边乘上\(c/g\),即\(-k_1'\)乘上\(c/g\)就得到了\(-k_1\).这里可以让\(-k_1\)对\(m_2\)取模,防止其过大.
  • 然后代入第一个方程求出\(x_0=a_1+k_1*m_1\),注意上一步求出的是\(-k_1\),这里要取负号.
  • 这两个方程的通解即为\(x=x_0+lcm(m_1,m_2)\),写成同余式为\(x\equiv x_0(mod\ lcm(m_1,m_2)).\)
  • 新得到的同余式和原来的两个同余式并在一起是等价的,那么将新得到的同余式继续按照上述步骤与其他同余式合并,直到最后只剩下一个同余式,就是要求的通解了.
  • 时间复杂度为\(O(n\cdot log(\prod m_i)).\)

应用

  • 解线性同余方程组,合并答案.

题目

Strange Way to Express Integers

  • 题意:求解一个线性同余方程组的最小正整数解,不保证模数互质.
  • 使用 \(excrt\) 处理即可.注意调整解.
#include"bits/stdc++.h"
#define int long long
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
const int MAXN=1e5+10;
int a[MAXN],m[MAXN],n;
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return d;
}
int excrt(int a[],int m[],int n)
{
int k1,k2;
for(int i=2;i<=n;++i)
{
int g=exgcd(m[i],m[1],k2,k1);
int c=a[1]-a[i];
if(c%g)
return 0;
k1*=c/g;
k1%=m[i];
a[1]=a[1]+(-k1)*m[1];//注意修改的先后顺序
m[1]=m[1]*m[i]/g;
}
return 1;
}
signed main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
m[i]=read(),a[i]=read();
int flag=excrt(a,m,n);
if(!flag)
puts("-1");
else
printf("%lld\n",(a[1]%m[1]+m[1])%m[1]);
}
return 0;
}

屠龙勇士

  • 题面比较长...直接看链接内容即可(注意数据表格).
  • 每轮剑的攻击力可以用一个 \(multiset\) 维护,可以直接预处理出来,记每轮攻击的攻击力为\(atk_i\).
  • 考虑记要求的攻击次数为 \(x\) ,则每次攻击为使恢复若干次后生命值恰好为 \(0\) ,只需满足\(atk_i*x\geq a_i,atk_i*x \equiv a_i(mod\ p_i).\)
  • 注意到数据中要么满足特性 \(1\) , 即\(a_i\leq p_i\),要么满足所有的 \(p_i=1\).
  • 那么可以判断后分情况处理,对于满足特性 \(1\) 的部分,显然只需处理同余式,同余式成立的情况下一定有\(atk_i*x\geq a_i\).对于所有 \(p_i=1\) 的部分,只需求出将每只龙的生命值打到非正数的次数,取最大值即可,即\(\max \lceil a_i/atk_i \rceil\),容易处理.
  • 对于 \(a_i\leq p_i\) 的部分,我们得到若干个同余式,考虑使用 \(excrt\) 进行处理.
  • 同余式形式是 \(atk_i*x \equiv a_i(mod\ p_i)\) ,需要先转化为 \(excrt\) 的标准形式,即

\[\begin{equation}\left\{ \begin{array}{lr} x\equiv a_1(mod\ m_1),\\ x\equiv a_2(mod\ m_2),\\ x\equiv a_3(mod\ m_3),\\ ......\\ x\equiv a_n(mod\ m_n).\\ \end{array}\right.\end{equation}
\]

  • 考虑将同余式两边乘上逆元 \(atk_i^{-1}\),可得到\(x \equiv atk_i^{-1}*a_i(mod\ p_i)\),就化为了标准形式.
  • 但 \(atk_i\) 与 \(p_i\) 可能不互质,此时 \(atk_i\) 是没有逆元的.可以根据同余的消去律,将\(atk_i,a_i,p_i\)都约去三者的 \(gcd\) .若此时 \(atk_i,p_i\) 仍不互质,则整个方程组也就无解了.否则求出逆元后,转化为标准形式.最后对所有转化后的同余式使用 \(excrt\) 求解.
  • 注意中间运算会出现$long\ long\ *\ long\ long \ % \ long\ long $的情况,使用快速乘,即将快速幂中的乘法运算改为加法运算,中途取模.
#include"bits/stdc++.h"
#define int long long
using namespace std;
typedef long long LoveLive;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
multiset<int> sword;
multiset<int>::iterator it;
int n,m;
const int MAXN=1e5+10;
int fmul(int a,int b,int mod)
{
assert(mod>0);
a=(a%mod+mod)%mod;
b=(b%mod+mod)%mod;
int res=0;
while(b)
{
if(b&1)
res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-(a/b)*y;
return d;
}
int a[MAXN],p[MAXN],atk[MAXN];
int award[MAXN];
bool check()
{
for(int i=1;i<=n;++i)
if(p[i]!=1)
return false;
return true;
}
void pre()
{
for(int i=1;i<=n;++i)
{
it=sword.upper_bound(a[i]);
if(it!=sword.begin())
--it;
atk[i]=*it;
sword.erase(it);
sword.insert(award[i]);
}
}
void solve_sp()
{
int ans=-1;
for(int i=1;i<=n;++i)
{
ans=max(ans,(a[i]+atk[i]-1)/atk[i]);
}
printf("%lld\n",ans);
}
int A[MAXN],M[MAXN];
int inv(int k,int mod)
{
int x,y;
exgcd(k,mod,x,y);
return (x%mod+mod)%mod;
}
bool build_equations()
{
int x,y;
for(int i=1;i<=n;++i)
{
int g=exgcd(a[i],p[i],x,y);
g=exgcd(g,atk[i],x,y);
atk[i]/=g;
a[i]/=g;
p[i]/=g;
int flag=exgcd(atk[i],p[i],x,y);
if(flag!=1)
return false;
A[i]=fmul(a[i],inv(atk[i],p[i]),p[i]);
M[i]=p[i];
}
return true;
}
void excrt()
{
int k1,k2;
for(int i=2;i<=n;++i)
{
int g=exgcd(M[i],M[1],k2,k1);
int c=A[1]-A[i];
if(c%g)
{
puts("-1");
return;
}
k1=fmul(k1,c/g,M[i]);
/*k1*=c/g;
k1%=M[i];*/
A[1]=A[1]-fmul(k1,M[1],M[1]*(M[i]/g));//打括号,防止爆long long
M[1]=M[1]*(M[i]/g);
A[1]%=M[1];
}
printf("%lld\n",(A[1]%M[1]+M[1])%M[1]);
}
signed main()
{
// freopen("dragon8.in","r",stdin);
int T=read();
while(T--)
{
sword.clear();
n=read();
m=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
p[i]=read();
for(int i=1;i<=n;++i)
award[i]=read();
for(int i=1;i<=m;++i)
sword.insert(read());
pre();
if(check())
{
solve_sp();
continue;
}
if(build_equations())
excrt();
else
puts("-1");
}
return 0;
}

crt,excrt学习总结的更多相关文章

  1. CRT&EXCRT学习笔记

    非扩展 用于求解线性同余方程组 ,其中模数两两互质 . 先来看一看两个显然的定理: 1.若 x \(\equiv\) 0 (mod p) 且 y \(\equiv\) 0 (mod p) ,则有 x+ ...

  2. CRT & EXCRT 学习笔记

    这玩意解决的是把同余方程组合并的问题. CRT的核心思想和拉格朗日插值差不多,就是构造一组\(R_i\)使得$\forall i,j(i \neq j) $ \[R_im_i = 1, R_im_j ...

  3. 扩展中国剩余定理 exCRT 学习笔记

    前言 由于 \(\{\mathrm{CRT}\}\subseteq\{\mathrm{exCRT}\}\),而且 CRT 又太抽象了,所以直接学 exCRT 了. 摘自 huyufeifei 博客 这 ...

  4. [笔记] CRT & exCRT

    [笔记] CRT & exCRT 构造法 求多组\(x \equiv r_i (\bmod d_i)\)的解,\(d_i\)互质 余数\((r_i = remainder)\),除数\((d_ ...

  5. CRT和EXCRT学习笔记

    蒟蒻maomao终于学会\(CRT\)啦!发一篇博客纪念一下(还有防止忘掉) \(CRT\)要解决的是这样一个问题: \[x≡a_1​(mod m_1​)\] \[x≡a_2​(mod m_2​)\] ...

  6. CRT&EXCRT 中国剩余定理及其扩展

    前言: 中国剩余定理又名孙子定理.因孙子二字歧义,常以段子形式广泛流传. 中国剩余定理并不是很好理解,我也理解了很多次. CRT 中国剩余定理 中国剩余定理,就是一个解同余方程组的算法. 求满足n个条 ...

  7. CRT && exCRT模板

    CRT从各种方面上都吊打exCRT啊...... 短,好理解... 考虑构造bi使得bi % pi = ai,bi % pj = 0.然后全加起来就行了. 显然bi的构造就是ai * (P/pi) * ...

  8. [note]CRT&exCRT

    中国剩余定理 别人的blog 假设现在有关于x的同余方程组(p1,p2均为质数) \(x=a_1\pmod {p_1}\) \(x=a_2\pmod {p_2}\) 可以转化成如下形式 \(x=a_1 ...

  9. 「中国剩余定理CRT」学习笔记

    设正整数$m_1, m_2, ... , m_r$两两互素,对于同余方程组 $x ≡ a_1 \ (mod \ m_1)$ $x ≡ a_2 \ (mod \ m_2)$ $...$ $x ≡ a_r ...

随机推荐

  1. 创建Jmeter中使用的jar包中的工具类方法

    1. 在IDEA中新建一个maven工程. 2. 编写工具类方法,如加密算法.此处以加法为例. package DemoTest; public class DemoClass{ public int ...

  2. 关于java 线程池 ThreadPoolExceutor 之 TestDemo

    public class App { public static void main(String[] args) throws InterruptedException { System.out.p ...

  3. git rm -r --cache命令 及 git .gitignore 文件

    git 的  .gitignore 文件的作用是在代码提交时自动忽略一个文件.不将其纳入版本控制系统. 比如.一般我们会忽略IDE自动生成的配置文件等. 如果一个你要忽略的文件已经纳入到了git ,也 ...

  4. angularjs控制器之间的数据共享与通信

    1.可以写一个service服务,从而达到数据和代码的共享; var app=angular.module('app',[]); app.service('ObjectService', [Objec ...

  5. Chrome自动化搭建

    工具安装: 1.selenium-java-2.53.0-srcs.jar 2.selenium-java-2.53.0.jar 3.下载chromedriver.exe(下载地址:http://ch ...

  6. PIL中文文档

    (0)http://hereson.iteye.com/blog/2224334 (1)http://blog.csdn.net/yjwx0018/article/details/52852067 ( ...

  7. DSP基础学习-ADC采样

    DSP基础学习-ADC采样 彭会锋 2015-04-27 22:30:03 在查看ADC采样例程的时候我发现了下面的代码挺有意思的 EALLOW; GpioCtrlRegs.GPAMUX2.bit.G ...

  8. java7与java8的新特性

    java7 新特性: 1. switch 里面的 case 条件可以使用字符串了. 2. 运用 List\tempList = new ArrayList<>(); 即泛型实例化类型自动判 ...

  9. 上传组件UploadiFive(H5版本)

    初始化 $('#file_upload').uploadifive({ 'auto' : false, 'buttonClass':'btn', 'buttonText':'选择视频', 'fileS ...

  10. mail_location not set and autodetection failed 解决方案[devecot, sendmail]

    安装dovecot比较简单, 但是也需要配置, 如果不进行任何配置时,在测试时会出现如下的提示: dovecot: pop3(wwufengg): Error: user wwufengg: Init ...