数论
数论
Lucas 定理
对于素数 \(p\) 有
CRT
\(m_1,m_2,...\) 两两互质,求解 \(x\)
定义 \(M=\prod m_i,M_i=\frac{M}{m_i},a_i\equiv M_i^{-1}\pmod {m_i}\)
则
exLucas 定理
与平常的 Lucas 想比 \(p\) 不是素数。
一般的,对于模数是合数的问题,我们一般都是给他变成素数相乘的形式做。
那么我们就可以求出来 \(\bmod a_i^{k_i}\) 这样的形式, 然后就转化为 CRT 问题了。
欧拉定理
我们知道 \(a^{p-1}\equiv1\pmod p\) 这个东西可以由欧拉定理 \(a^{\varphi(p)}\equiv1\pmod p\)(\(a,p\) 互质)
显然,这个东西他是一堆数然后拼成了一个环(群论)。
ex欧拉定理
扩展欧拉定理是要求 \(a^b\bmod m\) 等于多少(这和欧拉定理有啥区别)
显然的 \(a^b\equiv a^{b\bmod\varphi(m)}\pmod m\)
阶
阶的定义:当 \(\gcd(a,m)=1\) 时 \(a^{\varphi(m)}\equiv1\pmod m\) 若 \(a^{k}\equiv1\pmod m\) 则 \(k_{min}\) 为阶 \(k=\operatorname{ord}_m(a)\)。
显然有 \(\operatorname{ord}_m(a)\mid \varphi(m)\) 然后我们就可以通过质因数分解之类的来 \(O(\log_2a)\) 做了
原根
原根就是在阶的基础上 \(\operatorname{ord}_m(a)=\varphi(m)\) 的 \(a\) 就是原根。
当 \(m=1,2,4,p^a,2p^a\) 其中 \(p\) 是奇素数。
求原根没有什么好的办法直接暴力求,问题怎么判断是不是原根,同求阶的方法就行。
性质:当 \(m\) 为质数时,\(\{g^0,g^1,...,g^{m-2}\}\) 为 \(1\sim m-1\) 的排列
例题 \(1\):P6091
这道题教我们如何快速求原根,先判掉没有原根的数数,然后可以先求出最小的原根 \(g\),时间复杂度是 \(O(n^{0.25}\log_2n)\) (不会证明)。
有个推论:若有原根,则这个数的原根数量为 \(\varphi(\varphi(n))\)
接下来,若 \(\gcd(k,\varphi(n))=1\) 则 \(g^k\) 为 \(g\) 的原根,正确性很显然,用欧拉定理就可以得出,唯一性可以发现假设(为了方便) \(k\) 为第一个互质的,那么 \(g^{(1,k)}\) 绝对不成立(欧拉定理),设存在一个 \(t\) 使得他不是 \(k\) 中的一个,因为在环中,绝对有 \(g\equiv t^u\pmod n\) 然后感觉这个东西就很逆元,可以反证出来。
\(\mathscr{Code:}\)
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
// #define int ll
#define pii pair<int,int>
#define ull unsigned long long
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
char buf[1<<21], *p1=buf,*p2=buf;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#define getchar() gc()
ll rd(){
ll s=0; char ch=getchar(); bool fu=0;
while(ch<'0'||ch>'9')ch=='-'?fu=1:0,ch=getchar();
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return fu?-s:s;
}
const int N=1e6+10;
int phi[N],pr[N],cnt;
bool vis[N],st[N];
void init(){
phi[1]=1;
for(int i=2;i<N;++i){
if(!vis[i])pr[++cnt]=i,phi[i]=i-1;
for(int j=1;1ll*pr[j]*i<N;++j){
vis[i*pr[j]]=true;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j];
break;
}
phi[i*pr[j]]=phi[i]*(pr[j]-1);
}
}
st[2]=st[4]=1;
for(int i=2;i<=cnt;++i){
ll p = pr[i];
for(ll j=p;j<N;j*=p) st[j]=1;
for(ll j=2*p;j<N;j*=p) st[j]=1;
}
}
int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
ll qpow(ll a,int b,int mod){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
int tot,p[N];
void calc(int x){
tot=0;
for(int i=2;i*i<=x;++i){
if(x%i==0){
p[++tot]=i;
while(x%i==0)x/=i;
}
}
if(x>1)p[++tot]=x;
}
bool check(int x,int n){
if(qpow(x,phi[n],n)!=1)return 0;
for(int i=1;i<=tot;++i)
if(qpow(x,phi[n]/p[i],n)==1)return 0;
return 1;
}
int run(int n){
if(n==2)return 1;
if(n==4)return 3;
calc(phi[n]);
for(int i=2;i<n;++i)
if(check(i,n))return i;
return 0;
}
int ans[N],res;
void getans(int gen,int p){
res=0;
int cur=1;
for(int i=1;i<=phi[p];++i){
cur=cur*gen%p;
if(gcd(i,phi[p])==1)ans[++res]=cur;
}
}
inline void Solve(){
int n=rd(),d=rd();
if(!st[n]){
puts("0\n");
return;
}
int gen=run(n);
getans(gen,n);
sort(ans+1,ans+res+1);
printf("%d\n",res);
for(int i=1;i<=res/d;++i)
printf("%d ",ans[i*d]);
puts("");
}
signed main(){
// freopen("input.in","r",stdin);
// ios::sync_with_stdio(false);
// cin.tie(0), cout.tie(0);
init();
int T=rd();
while(T--)Solve();
return 0;
}
BSGS
问题自己看,那么显然有 \(0\le l\le p-1\)
那么我们可以分解成 \(l=iT+j\) 若 \(i\in[0,T-1],j\in[0,T-1]\) 则我们可以表示出来 \(l\in[0,T^2-1]\)
那么就好做了。
有 \(a^{iT}\equiv ba^{-j}\)
桶装即可。
整除分块
首先可以转化成 \(\sum (k-i\lfloor\frac{k}{i}\rfloor)=nk-\sum i\lfloor\frac{k}{i}\rfloor\)
求右面的式子可以将其划分为 \(i\le T\) 和 \(T<i\) 第一部分重复少但个数少,第二部分重复多但个数多,所以我们对于第一部分直接枚举,第二部分只有 \(\frac{k}{T}\) 种数,所以直接枚举数就行。
void Solve() {
rd(n, k);
ans = n * k, n = min(n, k);
function<ll(int, int)> sum = [&](int l, int r) {
return 1ll * (l + r) * (r - l + 1) >> 1;
};
for (int l = 1, r, cur; l <= n;) {
r = min(n, k / (cur = k / l));
ans -= sum(l, r) * cur;
l = r + 1;
}
printf("%lld\n", ans);
}

浙公网安备 33010602011771号