crt,excrt学习总结
\(crt,Chinese\ Remainder\ Theorem\)
概述
- 前置技能:同余基础性质,\(exgcd\).
- \(crt\),中国剩余定理.用于解决模数互质的线性同余方程组.大概长这样:
\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\) 是通过将方程两两合并,直到最后只剩下一个方程,也就得到了通解.
- 对于如下方程组:
\]
- 先考虑合并前两个方程,将其写成一个等价的线性同余方程.
- 由同余的基本性质可知,一个合法解 \(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\) 的标准形式,即
\]
- 考虑将同余式两边乘上逆元 \(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学习总结的更多相关文章
- CRT&EXCRT学习笔记
非扩展 用于求解线性同余方程组 ,其中模数两两互质 . 先来看一看两个显然的定理: 1.若 x \(\equiv\) 0 (mod p) 且 y \(\equiv\) 0 (mod p) ,则有 x+ ...
- CRT & EXCRT 学习笔记
这玩意解决的是把同余方程组合并的问题. CRT的核心思想和拉格朗日插值差不多,就是构造一组\(R_i\)使得$\forall i,j(i \neq j) $ \[R_im_i = 1, R_im_j ...
- 扩展中国剩余定理 exCRT 学习笔记
前言 由于 \(\{\mathrm{CRT}\}\subseteq\{\mathrm{exCRT}\}\),而且 CRT 又太抽象了,所以直接学 exCRT 了. 摘自 huyufeifei 博客 这 ...
- [笔记] CRT & exCRT
[笔记] CRT & exCRT 构造法 求多组\(x \equiv r_i (\bmod d_i)\)的解,\(d_i\)互质 余数\((r_i = remainder)\),除数\((d_ ...
- CRT和EXCRT学习笔记
蒟蒻maomao终于学会\(CRT\)啦!发一篇博客纪念一下(还有防止忘掉) \(CRT\)要解决的是这样一个问题: \[x≡a_1(mod m_1)\] \[x≡a_2(mod m_2)\] ...
- CRT&EXCRT 中国剩余定理及其扩展
前言: 中国剩余定理又名孙子定理.因孙子二字歧义,常以段子形式广泛流传. 中国剩余定理并不是很好理解,我也理解了很多次. CRT 中国剩余定理 中国剩余定理,就是一个解同余方程组的算法. 求满足n个条 ...
- CRT && exCRT模板
CRT从各种方面上都吊打exCRT啊...... 短,好理解... 考虑构造bi使得bi % pi = ai,bi % pj = 0.然后全加起来就行了. 显然bi的构造就是ai * (P/pi) * ...
- [note]CRT&exCRT
中国剩余定理 别人的blog 假设现在有关于x的同余方程组(p1,p2均为质数) \(x=a_1\pmod {p_1}\) \(x=a_2\pmod {p_2}\) 可以转化成如下形式 \(x=a_1 ...
- 「中国剩余定理CRT」学习笔记
设正整数$m_1, m_2, ... , m_r$两两互素,对于同余方程组 $x ≡ a_1 \ (mod \ m_1)$ $x ≡ a_2 \ (mod \ m_2)$ $...$ $x ≡ a_r ...
随机推荐
- u-boot-2015.07 make xxx_config 分析
1.u-boot编译脚本:mk.sh #! /bin/sh export PATH=$PATH:/opt/ti-sdk-am335x-evm-08.00.00.00/linux-devkit/sysr ...
- BZOJ4787/UOJ290 【ZJOI2017】仙人掌
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- Unity 和 3DMAx
3DMax转换成DAE模式才会有纹理信息.
- 使用Bind读取配置到C#的实例
在之前的一篇二级域名绑定的文章<.Net Core 二级域名绑定到指定的控制器>中,有一个小的地方是关于读取Json文件的配置信息的,当时是用了读取文件流的方式,一直以来觉得该方法太Low ...
- Ghost:一款简约风格博客系统
前言 本文将介绍一种最快速的创建Ghost博客系统的方法,并实现绑定二级域名到该博客系统.本文以本博客的“微博客”为例. 一键创建Ghost博客系统 Kite 是 Ghost 博客托管商,网址为:ht ...
- 1-27 awk 基本使用
大纲: 色彩: awk基本使用 ##################################################### 一.色彩:shell中,设置输出文本色彩(前景色,背景色) ...
- 转:25个Java机器学习工具和库
转自:http://www.cnblogs.com/data2value/p/5419864.html 本列表总结了25个Java机器学习工具&库: 1. Weka集成了数据挖掘工作的机器学习 ...
- Centos7下部署两套python版本并存
Centos7下部署两套python版本并存 需求说明:centos7.2系统的开发机器上已经自带了python2.7版本,但是开发的项目中用的是python3.5版本,为了保证Centos系统的 ...
- laravel怎么获取到public路径
app_path() app_path函数返回app目录的绝对路径: $path = app_path(); 你还可以使用app_path函数为相对于app目录的给定文件生成绝对路径: $path = ...
- [C#]ref,out关键字的作用
ref是传递参数的地址,out是返回值,两者有一定的相同之处,不过也有不同点. 使用ref前必须对变量赋值,out不用 out的函数会清空变量,即使变量已经赋值也不行,退出函数时所有out引用的变量都 ...