数学
等比数列求和公式
自然数幂和
可以使用拉格朗日插值求,复杂度 \(O(k)\)。
这里给出 \(k\leq 3\) 的公式:
拉格朗日插值求法:
以下是求 \(\sum_{i=1}^ni^k\)
int langrange(int n,int k){
n%=mod;
for(int i=1;i<=k+2;i++) y[i]=(y[i-1]+qpow(i,k))%mod;
int res=0;
pre[0]=1;suf[k+3]=1;
for(int i=1;i<=k+2;i++) pre[i]=pre[i-1]*(n-i+mod)%mod;
for(int i=k+2;i>=1;i--) suf[i]=suf[i+1]*(n-i+mod)%mod;
for(int i=1;i<=k+2;i++){
res=(res+((k+2-i)&1? -1: 1)*y[i]*inv[i-1]%mod*inv[k+2-i]%mod*pre[i-1]%mod*suf[i+1]%mod+mod)%mod;
}
return res;
}
裴蜀定理
若 \(\gcd(a,b)=d\),则对于任意 \(x, y\),\(ax+by\) 是 \(d\) 的倍数。
若 \(\gcd(a,b)=d\),则关于 \(x, y\) 的方程 \(ax+by=d\) 一定存在整数解。
Lucas定理
\(p\) 是质数
不能直接通过 fac[n]*inv[m]*inv[n-m]
求出,因为在 \(x\geq p\) 时,fac[x]
\(=0\)。
同样需要注意在预处理 fac[]
和 inv[]
时,循环上界必须为 \(p-1\),否则预处理的 inv[]
数组会全为 \(0\)。
扩展欧几里得算法
求解 \(ax+by=\gcd(a,b)\) 的一组解
void exgcd(long long a,long long b,long long &x,long long &y){
if(b==0){
x=1;y=0;//此时a是gcd
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
欧拉定理与费马小定理的对照
欧拉定理:\(a^{\varphi(p)}=1\bmod p,\gcd(a,p)=1\)
费马小定理:\(a^{p-1}=1\bmod p,p为质数\)
可以说费马小定理是欧拉定理的一种特殊形式
扩展欧拉定理
\(a^b\bmod\) \(p=\)
核心操作如下
for(int i=1;i<=len;i++){
x=x*10+(c[i]-'0');
x%=p;
}
x+=p;
cout<<qpow(a,x,mod);
质数筛
void prime(){
for(int i=2;i<=n;i++){
if(!in[i]) pr[++cnt]=i;
for(int j=1;j<=cnt&&pr[j]*i<=n;j++){
in[pr[j]*i]=1;
if(i%pr[j]==0) break;
}
}
}
欧拉函数
基本性质:
- 积性函数:\(\varphi(a\times b)=\varphi(a)\times \varphi(b),\) \(\gcd(a,b)=1 \\\)
- \(\varphi(p^k)=p^{k-1}\times (p-1)=p^k-p^{k-1} \\\)
- 有时根据需要,要表示成 \(\varphi(x)=\Pi_i k_i^{p_i}\) (所有 \(k_i\) 互质)
\(O(\sqrt n)\) 求单个数的 \(\varphi\) 值
原理:\(\varphi(p^k)=p^k-p^{k-1}=p^k\times(1-{1\over p})\),而 \(1-{1\over p}={p-1 \over p}\)
int Phi(long long x){
long long ans=x;
for(int i=2;i*i<=x;i++){//其实就是分解质因数的过程
if(x%i==0){
ans=ans*(i-1)/i;
while(x%i==0) x/=i;
}
}
if(x>1) ans=ans*(x-1)/x;//可能最后x也是个待分解的质数
return ans;
}
线性筛欧拉函数
void Phi(){
ph[1]=1;
for(int i=2;i<=n;i++){
if(!in[i]){
ph[i]=i-1;
a.push_back(i);
}
for(int j=0;j<a.size();j++){
if(a[j]*i>n) break;
in[a[j]*i]=1;
if(i%a[j]==0){
ph[i*a[j]]=ph[i]*(a[j]);
break;
}
else ph[a[j]*i]=ph[a[j]]*ph[i];
}
}
}
莫比乌斯函数
\(\mu(n)\)=
线性筛 \(\mu(n)\)
void Mu(){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!in[i]){
pr.push_back(i);mu[i]=-1;
}
for(auto x:pr){
if(x*i>n) break;in[x*i]=1;
if(i%x==0){
mu[i*x]=0;break;
}
else mu[i*x]=mu[i]*mu[x];
}
}
}
高斯消元
for(int i=1;i<=n;i++){
double mx=0;
int to;
for(int j=i;j<=n;j++){
if(abs(a[j][i])>mx){
mx=abs(a[j][i]);
to=j;
}
}
if(mx<eps) continue;
swap(a[i],a[to]);
for(int j=1;j<=n;j++){
if(j==i) continue;
double p=a[j][i]/a[i][i];
for(int k=1;k<=n+1;k++){
a[j][k]-=p*a[i][k];
}
}
}
for(int i=1;i<=n;i++){
if(abs(a[i][i])<eps){
cout<<"No Solution";
return 0;
}
}
for(int i=1;i<=n;i++){
printf("%.2lf\n",a[i][n+1]/a[i][i]);
}
中国剩余定理
求解形如
方程组,其中 \(p_i\) 两两互质。
构造法:
设 \(M=\prod_{i=1}^np_i\),令 \(M_i={M\over p_i}\),\(t_i={1\over M_i}\bmod p_i\)。
那么就可以构造出一个解:\(x=\sum_{i=1}^n a_i\times M_i t_i\)。
可以发现:当 \(k \not=i\) 时,\(a_i\times M_i t_i\bmod p_k=0\),因为 \(M_i\) 中含有 \(p_k\) 这个因子。
当 \(k=i\) 时,\(a_k\times M_i t_i\equiv a_k\bmod p_k\),因为在 \(\bmod p_k\) 意义下,\(M_i t_i \equiv 1\)。
该方程的通解为:\(x=\sum_{i=1}^n a_i\times M_i t_i+kM\)
最小解为:\(x=\sum_{i=1}^n a_i\times M_i t_i\bmod M\)
注意:不能使用费马小定理求逆元,因为 \(p_i\) 互质不等同于 \(p_i\) 都为质数。
void exgcd(long long a,long long b,long long &x,long long &y){
if(!b){
x=1;y=0;return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
void solve(){
long long ans=0;
for(int i=1;i<=n;i++){//inv是M_i,s[n]是M
long long inv,_;
exgcd(s[n]/a[i],a[i],inv,_);
inv=(inv+a[i])%a[i];
long long res=s[n]/a[i]*b[i]%s[n]*inv%s[n];
ans+=res;
}
cout<<ans%tot;
}
扩展中国剩余定理
采用逐个求解法。
设已经求解了前几个方程组,\(M\) 为前几个方程组模数的 \(\operatorname{lcm}\),通解为 \(x=r_1+kM\)。
考虑下一个方程:\(x=r_2\bmod p_i\),设 \(M\prime =\operatorname{lcm}(M,p_{now})\),求解完这个方程后的答案为 \(x=r_1+tM+kM\prime\),其中 \(t\) 是我们需要求解的量。
方程为 \(r1+tM+kp_{now}=r2\),整理得 \(tM+kp_{now}=r2-r1\)。
根据裴蜀定理,方程有解的条件是 \(\gcd(M,p_{now})|(r2-r1)\)。
然后使用扩展欧几里得算法即可求出答案。
为了避免中间结果爆 long long
,需要使用 __int128
。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const __int128 bs=1;
const int N=1e5+5;
int lcm(int a,int b){
return bs*a*b/__gcd(a,b);
}
int lm;
int r1;
void exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
int a[N],b[N];//a[i]为模数,b[i]为模后的值
int n;
void exCRT(){
lm=1;r1=0;
for(int i=1;i<=n;i++){
int g=__gcd(lm,a[i]);
int x=0,y=0;
if((b[i]-r1)%g!=0){//b[i]指文章中的r2
cout<<-1;return;
}
exgcd(lm/g,a[i]/g,x,y);
r1=(r1+bs*(b[i]-r1)/g*x*lm)%lcm(lm,a[i]);//__int128防溢出
lm=lcm(lm,a[i]);
}
cout<<(r1+lm)%lm;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
exCRT();
return 0;
}
杜教筛
用于求数论函数前缀和
原理:设
那么
进一步变形:
最终式子为:
等号右端的式子可以使用整除分块处理,然后可以递归求解。
一般先预处理一部分,然后其余的记忆化处理,时间复杂度 \(O(n^{2\over 3})\)
注意:必须保证 \(\sum_{i=l}^r(f * g)(i)\) 能够 \(O(1)\) 求出,才能使用杜教筛。