数论总结
cf906D-exgcd
题面:
\[给定一个数列w_1,w_2,...,w_n和模数p,每次询问一个区间[l,r],求w_l^{w_{l+1}^{w_{l+2}^{{...}^{w_r}}}} mod p
\]
\[w_1,w_2,...,w_n
\]
\[w_l^{w_{l+1}^{w_{l+2}^{{...}^{w_r}}}} mod p
\]
使用扩展欧拉定理
\[a^b=a^{b mod f(p)+f(p)}(b>f(p))
\]
\[a^b=ksm(a,b,p)(b≤f(p))
\]
递归的,如果我们要处理原问题,转换为
\[ksm(w_l,kmodf(p)+f(p),p),f(p)是欧拉函数
\]
从而转换为求k的过程
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsinged long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
#define debugg(x) cout<<#x<<"="<<x<<endl;
#define debug1(x,y) cout<<#x<<' '<<#y<<' '<<x<<' '<<y<<endl;
#define debug cout<<endl<<"*********"<<endl;
#define itn int
#define pii pair<int,int>
#define mid ((all[now].lo+all[now].ro)/2)
#define mod m1
map<ll,ll> eu;
void fre(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
}
void fc(){fclose(stdin);fclose(stdout);}
const int maxn=1e5+10;
const ll inf=0x7fffffff;
ll n,m,q,m1;
ll yuan[maxn];
ll ksm(ll a,ll ci,ll mods){
ll ans=1;
while(ci>0){
if(ci%2==1){
ans=ans*a;
if(ans>mods) ans=ans%mods+mods;//exeuler
}
ci/=2;a=a*a;
if(a>mods) a=a%mods+mods;
}
return ans;
}
ll gcd(ll a,ll b){while(b^=a^=b^=a%=b);return a;}
ll findf(ll now){
ll eu=now;
for(int i=2;i<=sqrt(now);i++){
if(now%i==0) eu=eu-eu/i;
while(now%i==0) now/=i;
}
if(now>1) eu=eu-eu/now;
return eu;
}
ll dfs(ll lo,ll ro,ll mods){
if(mods==1) return 1;
if(lo==ro) return (yuan[lo]>mods)?yuan[lo]%mods+mods:yuan[lo];//exeuler
return ksm(yuan[lo],dfs(lo+1,ro,eu[mods]),mods);
}
int main(){
ios;cin>>n>>m;m1=m;
for(int i=1;i<=n;i++) cin>>yuan[i];
while(m!=1){eu[m]=findf(m);m=eu[m];}
eu[1]=1;
cin>>q;
for(int i=1;i<=q;i++){
ll a1,a2;cin>>a1>>a2;
cout<<dfs(a1,a2,m1)%m1<<endl;//最表层的递归ksm只需要modp不需要%p+p(modp等效但直接输出会多k*p)
}
return 0;
}
3811-求逆元的多种方法
问题是求满足
\[1/a=x(modp)
\]
的x
1. 扩展欧几里得
\[1=ax(modp)--->ax+yp=1
\]
exgcd(a,b)能够求出形如ax+by=gcd(a,b)的最小整数解
ll x1,y;
void exgcd(ll a,ll b){
if(!b){x1=1;y=0;return;}
exgcd(b,a%b);
ll tmp=x1;x1=y;y=tmp-a/b*y;
}
注意此时得到的x1并不一定是正整数,需使用同余状态下最小整整数转换公式
x1=(x1%b+b)%b;
\[负数取模 -10\%3=-10-\lceil-10/3\rceil\times3=-1
\]
2. 费马小定理
\[a^{p-1}=1(modp),当且仅当p为质数
\]
欧拉定理
\[a^{f(p)}=1(modp),当且仅当gcd(a,p)=1
\]
扩展欧拉定理(欧拉降幂公式)
\[a^b=a^{bmodf(p)}(modp),gcd(a,p)=1
\]
\[a^{bmodf(p)+f(p)}=a^b(modp),gcd(a,p)\neq1,b>f(p)
\]
\[a^b=a^b(modp),gcd(a,p)\neq1 ,b<f(p)
\]
求解逆元时运用费马小定理有
\[a^{p-2}=a^{-1}(modp)
\]
故只需ksm(a,p-2,p)即可,exgcd复杂度略优于ksm,适用于单个较大数逆元求解
3. 逆元递推公式
inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=(p-p/i)*inv[p%i]%p;
for(int i=1;i<=n;i++) cout<<inv[i]<<endl;
适用于[1,n](n较小)的逆元求解复杂度O(n)
1516-ax+by=c的最小非负整数解
\[k_{min}
=k_{j}mod\frac{l}{gcd(W,l)}
\]
求解ax+by=c的最小非负整数解
-
exgcd(a,b) 需保证a>0,b>0 式子变形后要调整正负(同时转变a,c符号,b待定参数符号改变)
-
得到的x1是ax+by=gcd(a,b)的最小非负整数解,应该化简ax+by=c
\[ans=((c/gcd(a,b)*k1)\%(b/gcd(a,b))+b/gcd(a,b))\%(b/gcd(a,b)) \]3807-Lucas定理
求解形如
\[C_{m}^{n}=x(modp) \]的关于x的方程
有
\[C_{m}^{n}=C_{m(mod)p}^{n(mod)p}\times C_{m/p}^{n/p}(modp) \]关于Ca1,a2 (a1<p,a2<p)(modp)可以预处理得出
\[C_{a1}^{a2}=a1!/(a2!\times (a1-a2)!)
\]
即我们预处理出
-
小于模数p的阶乘modp前缀和(此时有一个性质是所有前p-1项阶乘都不被p整除,且前p-1项值是1到p-1的一个排列)
-
小于模数p的所有数的逆元inv[i],此时有
\[ax=1(modp)-->a(xmodp)=ax=1(modp) \]所以逆元可以直接由inv[1-(p-1)]的值表示
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define endl '\n'
#define debug cout<<endl<<"*********"<<endl;
#define debugg(x) cout<<#x<<"="<<x<<endl;
#define itn int
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
void fre(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
}
void fc(){fclose(stdin);fclose(stdout);}
const int maxn=1e5+10;
const ll inf=0x7fffffff;
int n,m,p,t;
ll zhui[maxn];
int cntp[maxn];
ll inv[maxn];
ll x1,y;
void exgcd(ll a,ll b){
if(!b){x1=1;y=0;return;}
exgcd(b,a%b);
ll tmp=x1;
x1=y;y=tmp-a/b*y;
}
int ck(int a,int b){
if(b>a) return 0;//注意边界
return zhui[a]*(inv[zhui[b]])%p*inv[zhui[a-b]]%p;
}
int lucas(int n,int m){
if(!m) return 1;//边界
return lucas(n/p,m/p)*ck(n%p,m%p)%p;
}
int main(){
ios;fre();
cin>>t;
while(t--){
cin>>n>>m>>p;
memset(inv,0,sizeof(inv));inv[1]=1;
for(int i=2;i<p;i++) inv[i]=(p-p/i)*inv[p%i]%p;
ll ans=1;
zhui[0]=1;
for(int i=1;i<=p;i++) zhui[i]=zhui[i-1]*i%p;
cout<<lucas(n+m,n)<<endl;
}
return 0;
}
3868-中国剩余定理
求解方程组
\[x=a_{i}(mod(b_{i})),其中b_{i}两两互质
\]
的最小解
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) a[i]=(a[i]%b[i]+b[i])%b[i];
ll all=1,ans=0;
for(int i=1;i<=n;i++) all*=b[i];
for(int i=1;i<=n;i++){
ll bnow=all/b[i];//bnow与b是互质的
exgcd(bnow,b[i]);
x1=(x1%b[i]+b[i])%b[i];
ans+=ksc(ksc(a[i],x1,all),bnow,all);
ans%=all;
}
以及快速乘模版
inline ll ksc(ll a,ll b,ll mod){
ll ans1=0;
while(b>0){
if(b%2) ans1=(ans1+a)%mod;
a=(a+a)%mod;
b/=2;
}
return ans1;
}
4777-excrt
在中国剩余定理的形式基础上,如果bi不互质,需采用excrt
不会证明看题解区阮行止的证明
cin>>n;
for(int i=1;i<=n;i++) m[i]=read(),c[i]=read();
for(int i=2;i<=n;i++){
ll cha=((c[i]-c[i-1])%m[i]+m[i])%m[i];
ll gcd1=exgcd(m[i-1],m[i]);
if(cha%gcd1){cout<<"No solution.";break;}
x1=(x1%m[i]+m[i])%m[i];
c[i]=c[i-1]+x1*cha/gcd1%m[i]*m[i-1];
m[i]*=m[i-1]/gcd1;
c[i]=(c[i]%m[i]+m[i])%m[i];
}
print(c[n]);
__int128输入输出板子(注意__int128不能开cin优化)
inline __int128 read(){
__int128 x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void print(__int128 x){
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
2480-古代猪文
数论全家桶
实际上是求
\[G^{\sum [d|n]C^{d}_{n}}modp,p=999911659
\]
求上面的累加和modf(p)由于f(p)太大Lucas没法预处理,所以可以分解质因数f(p),2,3,4679,35617,分别使用Lucas求出取模后的值,再联立同余方程组使用crt求解最小整数解,然后使用ksm求得最终解。
另,需要特判Gmodp==0的情况,因为这里使用了扩展欧拉定理解决,
G==p时费马小定理/欧拉定理/扩展欧拉定理不成立
4549-裴蜀定理
ax+by=k*gcd(a,b)
注意使用gcd/exgcd/时需要保证x,y>0
欧拉函数
void getprime(){
memset(num,1,sizeof(num));
memset(eu,1,sizeof(eu));
num[0]=num[1]=0;eu[1]=1;
for(int i=2;i<=1e7;i++){
if(num[i]){prime[++cntpri]=i;eu[i]=i-1;}
for(int j=1;j<=cntpri&&prime[j]*i<=1e7;j++){
num[prime[j]*i]=0;
if(gcd(prime[j],i)==1) eu[prime[j]*i]=eu[prime[j]]*eu[i];
else eu[prime[j]*i]=prime[j]*eu[i];
if (i%prime[j]==0) break;
}
}
}
线性筛
for(int i=2;i<=sqrt(n);i++){
if(n%i==0) eu=eu*(1-1.0/i);
while(n%i==0) n/=i;
}
if(n!=1) eu=eu*(1-1.0/n);
O(√n)求出单个欧拉函数值
2568-求gcd为质数的小于n的有序数对,有
\[zhui[n/prime[j]]*2-1,注意到\varphi(1)=1,且数对为(1,1),此时n/prime[j]=1的数有(3,3),(7,7)等
\]
2158-求正方形点阵中最多能够连成的线的数目
可以发现gcd(x,y)≠1的点不能计入考虑,且具有对称性,
遍历每一个x坐标[2,n-1],对应的加上他的欧拉函数值,最后将前缀和*2+3(考虑垂直和水平线,以及y=x)可以得到答案
2398-
\[∑^n_{i=1}
∑_{j=1}^n
gcd(i,j)
\]
考虑到gcd为1的数就是∑f(i),i<=n的和*2,gcd为2的数就是∑f(i),i<=n/2的前缀和*2,,gcd最大为n,余数从1到n遍历,每一层循环内f(i)*yu<=n的加上即可。

浙公网安备 33010602011771号