数论(不)全家桶
高斯消元
const double zero=1e-6;//注意精度
int n;
double a[maxn][maxn];
int gs(){
int c,line,pai=0;
for(c=1;c<=n;c++){
line=c;
for(int i=1;i<=n;i++){//找第i排第c项最大的 ,从1开始可以避免前面被忽略的
if(fabs(a[i][i]>zero)&&i<c) continue;// 该位有数且在第c行前就跳过
if(fabs(a[line][c])<fabs(a[i][c])) line=i;
}
for(int i=1;i<=n+1;i++) swap(a[line][i],a[c][i]);
if(fabs(a[c][c])<zero) continue;
for(int i=n+1;i>=c;i--) a[c][i]=a[c][i]/a[c][c];//将(c,c)消为1
for(int i=c+1;i<=n;i++)
for(int j=n+1;j>=c;j--)
a[i][j]-=a[c][j]*a[i][c];//消掉所有行的c位
pai++;//解出的行数
}
for(int i=n;i>=1;i--)
for(int j=i-1;j>=1;j--) a[j][n+1]-=a[i][n+1]*a[j][i],a[j][i]=0;//消元得解
if(pai<n){//解不够
for(int i=1;i<=n;i++)
if(fabs(a[i][i])<zero&&fabs(a[i][n+1])>zero) return 1;//(≠0)=0,无解
return 2; //无穷解 0=0
}
else{
for(int i=1;i<=n;i++){
if(fabs(a[i][n+1])<zero) printf("x%d=0\n",i);
else printf("x%d=%.2lf\n",i,a[i][n+1]);
}
return 0;
}
}
}
const double jd=1e-6;
double a[110][110];
bool solve(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) if(fabs(a[i][i])<fabs(a[j][i])&&a[j][j]!=1) swap(a[i],a[j]);//注意应从1开始
if(fabs(a[i][i])<jd) continue;
for(int j=n+1;j>=i;j--)
a[i][j]/=a[i][i];
for(int j=i+1;j<=n;j++)
for(int k=n+1;k>=i;k--)
if(a[j][i])
a[j][k]/=a[j][i],a[j][k]-=a[i][k];
}
for(int i=n;i>=1;i--)
for(int j=i-1;j>=1;j--)
{
a[j][n+1]-=a[i][n+1]*a[j][i];
a[j][i]=0;
}
for(int i=1;i<=n;i++)
if(fabs(a[i][i])<jd&&fabs(a[i][n+1])>jd){
cout<<-1;
return 0;
}
for(int i=1;i<=n;i++)
if(fabs(a[i][i])<jd&&fabs(a[i][n+1])<jd){
cout<<0;
return 0;
}
return 1;
}
线性筛
bool is_not_prime[maxn]={1,1};//0为是prime,1为不是prime
int prime[maxn];
void xxs(int n){
for(int i=2;i<=n;i++){//从2开始推
if(!is_not_prime[i]) prime[++prime[0]]=i;//是prime,加入prime数组
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){//筛掉prime[j]的i倍(不是素数)
is_not_prime[i*prime[j]]=1;
if(i%prime[j]==0) break;//已筛过,避免重复筛
}
}
}
欧拉函数
求小于等于某个数的范围内与该数互质的数的个数
求单个数
int eular_phi(int n) {
int m=sqrt(n+0.5);//防止精度问题
int ans=n;
for(int i=2;i<=m;i++){
if(n%i==0){
ans=ans/i*(i-1);//定义法+避免出现浮点数
while(n%i==0) n/=i;//减小搜索范围,筛去非素数
}
}
if(n>1) ans=ans/n*(n-1);//可能n最后为素数
return ans;
}
求范围内数的欧拉函数
bool is_not_prime[maxn];//0为是prime,1为不是prime
int prime[maxn],phi[maxn];
void eu_phi(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!is_not_prime[i]){//是素数
prime[++prime[0]]=i;
phi[i]=i-1;//性质
}
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
is_not_prime[i*prime[j]]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=prime[j]*phi[i];//变形
break;
}
else phi[i*prime[j]]=(prime[j]-1)*phi[i];//变形
}
}
}
欧拉函数求约数个数
bool is_not_prime[maxn];//0为是prime,1为不是prime
int prime[maxn],num[maxn],d[maxn],n,ans;//d[]记录约数个数,num[]记录最小质因数的次幂
void eu_phi(int n){
d[1]=1;
for(int i=2;i<=n;i++){
if(!is_not_prime[i]){//是素数
prime[++prime[0]]=i;
num[i]=1;//最小质因子就为其本身,次数为1
d[i]=2;//约数只有两个
}
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
is_not_prime[i*prime[j]]=1;
if(i%prime[j]==0){
d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2);//i的最小质因子为prime[j],i*prime[j]的最小质因子也为prime[j],次数为num[i]+1
num[i*prime[j]]=num[i]+1;//d[i]=(1+r1)*(1+r2)*...*(1+rk),d[i*prime[j]]=(1+r1+1)*...*(1+rk) →→d[i*prime[j]]=d[i]/(1+num[i])*(1+num[i]+1)
break;
}
else{
d[i*prime[j]]=d[i]*2;//不能整除则之前不含prime[j],由于j从小到大枚举,prime[j]最小,所以d[i*prime[j]]=d[i]*(1+1)
num[i*prime[j]]=1;
}
}
}
}
扩展欧几里得
int exgcd(int x_1,int y_1,int &x_2,int &y_2){
if(!y_1){
x_2=1;
y_2=0;
return x_1;
}
int a,b;
int s=gcd(y_1,x_1%y_1,a,b);
x_2=b;
y_2=a-x_1/y_1*b;
return s;
}
乘法逆元
扩展欧几里得
#include<bits/stdc++.h>
using namespace std;
int a,b,x,y;
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return a;
}
int kk=exgcd(b,a%b,x,y);
int tt=x;
x=y;
y=(tt-a/b*y);
return kk;
}
int main(){
std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>a>>b;
for(int i=1;i<=a;i++){
exgcd(i,b,x,y);
cout<<((x%b)+b)%b<<'\n';
}
return 0;
}
线性求1~n
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int p,inv[3000001],n;
int main(){
std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>p;
inv[1]=1;
cout<<1<<'\n';
for(int i=2;i<=n;i++){
inv[i]=(ll)(p-p/i)*inv[p%i]%p;
cout<<inv[i]<<'\n';
}
return 0;
}
扩展欧拉
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a,m,b,phi,len;
string ge;
int gcd(int x,int y){
if(x<y) swap(x,y);
if(y==0) return x;
return gcd(y,x%y);
}
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1) ans=1ll*ans*x%m;
x=1ll*x*x%m;
y>>=1;
}
return ans;
}
inline int read()
{
int ans=0;
for(int i=0;i<(int)ge.size();i++){
ans=(ans*10+ge[i]-'0')%phi;
}
return ans;
}
inline int read1()
{
int ans=0;
for(int i=0;i<(int)ge.size();i++){
ans=(ans*10+ge[i]-'0');
}
return ans;
}
int phiq(int x){
int ans=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
ans=ans/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) ans=ans/x*(x-1);
return ans;
}
int main(){
cin>>a>>m;
int g=gcd(a,m);
phi=phiq(m);
int kk=phi;
while(kk){
len++;
kk/=10;
}
cin>>ge;
if(g==1) b=read();//满足gcd(a,m)==1则a^b%m==a^(b%phi(m))%m
else if((int)ge.size()>len){//若b>=phi(m)则a^b%m==a^(b%phi(m)+phi(m))%m
b=read();
if(g!=1) b+=phi;
}
else{
b=read1();
if(b>=phi) b=b%phi+phi;//若b>=phi(m)则a^b%m==a^(b%phi(m)+phi(m))%m
//若b<phi(m)则不作处理,且b不大
}
cout<<qpow(a,b);
return 0;
}
卢卡斯定理
用于超大数组合数取模,模数必须是素数。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,a,p;
int qpow(int x,int y,int mod){
int ans=1;
while(y){
if(y&1) ans=1ll*x*ans%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ans;
}
int C(int x,int y,int mod){
if(x<y) return 0;
int jx=1,jy=1,jz=1;
for(int i=1;i<=x;i++) jx=1ll*jx*i%mod;
for(int i=1;i<=y;i++) jy=1ll*jy*i%mod;
for(int i=1;i<=x-y;i++) jz=1ll*jz*i%mod;
return 1ll*jx*qpow(jy,mod-2,mod)%mod*qpow(jz,mod-2,mod)%mod;
}
int lucas(int x,int y,int mod){
if(!y) return 1;
return 1ll*C(x%mod,y%mod,mod)*lucas(x/mod,y/mod,mod)%mod;
}
signed main(){
cin>>t;
while(t--){
cin>>n>>m>>p;
cout<<lucas(n+m,n,p)<<'\n';
}
return 0;
}
中国剩余定理(CRT)
用于求解多模数意义下的线性同余方程,要求模数互质。
流程:
令余数为\(r\),模数为\(m\)。
- 1.模数求积\(S\)
- 2.令\(C_i\)为第i个模数\(S/m_i\) 的商,并求出逆元
- 3.\(ans=\sum_{i=1}^n r_i*C_i*C_i^{-1} ~mod~S\)。
#include<bits/stdc++.h>
#define int __int128
using namespace std;
typedef long long ll;
ll n,r[11],m[11];
int ans;
void exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return;
}
exgcd(b,a%b,x,y);
int kk=x;
x=y;
y=kk-a/b*y;
}
signed main(){
cin>>n;
__int128 S=1;
for(int i=1;i<=n;i++){
cin>>m[i]>>r[i];
S*=m[i];
}
for(int i=1;i<=n;i++){
int ny,y,c=S/m[i];
exgcd(c,m[i],ny,y);
ans=(ans+r[i]*c*ny%S)%S;
}
cout<<(ll)((ans%S+S)%S);
return 0;
}
扩展CRT
扩展欧几里得求同余方程
设两个方程分别是 \(x\equiv a_1 \pmod {m_1}、x\equiv a_2 \pmod {m_2};\)
将它们转化为不定方程:\(x=m_1p+a_1=m_2q+a_2\),其中 \(p, q\) 是整数,则有 \(m_1p-m_2q=a_2-a_1。\)
由 裴蜀定理,当 $a_2-a_1 $不能被 \(\gcd(m_1,m_2)\) 整除时,无解;
其他情况下,可以通过 扩展欧几里得算法 解出来一组可行解 \((p, q)\);
则原来的两方程组成的模方程组的解为 \(x\equiv b\pmod M\),其中 \(b=m_1p+a_1\),\(M=\text{lcm}(m_1, m_2)\)。
#include<bits/stdc++.h>
#define int __int128
using namespace std;
typedef long long ll;
ll n,r[100001],m[100001];
int ans;
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return a;
}
int g=exgcd(b,a%b,x,y);
int kk=x;
x=y;
y=kk-a/b*y;
return g;
}
int a,b,x,y;
signed main(){
while(scanf("%lld",&n)!=EOF){
memset(r,0,sizeof(r));
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++) cin>>m[i]>>r[i];
a=1,b=0;
bool p=0;
for(int i=1;i<=n;i++){
int g=exgcd(a,m[i],x,y);
if((b-r[i])%g){
cout<<-1<<'\n';
p=1;
break;
}
x=(b-r[i])/g*x;
b=b-a*x;
a=m[i]/g*a;
b=(b%a+a)%a;
}
if(!p) cout<<(ll)((b%a+a)%a)<<'\n';
}
return 0;
}

浙公网安备 33010602011771号