数论总结
欧拉定理
aφ(n)≡1(modn) a φ ( n ) ≡ 1 ( mod n )
φ(n) φ ( n ) 为欧拉函数
使用条件为gcd(a,n) = 1。
欧拉降幂公式:
ab≡ab%φ(n)+φ(n)(modn)(b>φ(n)) a b ≡ a b % φ ( n ) + φ ( n ) ( mod n ) ( b > φ ( n ) )
求欧拉函数:
//直接求phi
int get_phi(int c)
{
LL ans = c;
int m = sqrt(c+0.5);
for(int i = 2; i <= m; ++i)
{
if(c % i == 0)
{
ans = ans/i*(i-1);
while(c % i ==0)c/=i;
}
}
if(c > 1)ans = ans/c*(c-1);
return ans;
}
//线性筛法,快的飞起
bool vis[MAXN];
int prime[MAXN];
int phi[MAXN];
int tot;
void init()
{
memset(vis, false, sizeof(vis));
phi[1] = 1;
tot = 0;
for(int i = 2; i < MAXN; i ++)
{
if(!vis[i])
{
prime[tot ++] = i;
phi[i] = i - 1;
}
for(int j = 0; j < tot; j ++)
{
if(i * prime[j] >= MAXN) break;
vis[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else
{
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
}
}
素数筛:
void get_prim()
{
for(int i = 2; i < MAXN; ++i)
{
if(!vis[i])prim[tot++] = i;
for(int j = 0; j < tot; ++j)
{
if(prim[j]*i > MAXN)break;
vis[i*prim[j]] = 1;
if(i%prim[j] == 0)break;
}
}
}
Miller_Rabin 算法进行素数测试 pollard_rho 算法进行质因数分解
//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S = 10;
//计算x*y%c
LL modular_multi(LL x,LL y,LL mo)
{
LL t;
x%=mo;
for(t=0;y;x=(x<<1)%mo,y>>=1)
if (y&1)
t=(t+x)%mo;
return t;
}
//计算num^t%c
LL modular_exp(LL num,LL t,LL mo)
{
LL ret=1,temp=num%mo;
for(;t;t>>=1,temp=modular_multi(temp,temp,mo))
if (t&1)
ret=modular_multi(ret,temp,mo);
return ret;
}
bool miller_rabbin(LL n)
{
if (n==2)return true;
if (n<2||!(n&1))return false;
int t=0;
LL a,x,y,u=n-1;
while((u&1)==0) t++,u>>=1;
for(int i=0;i<S;i++)
{
a=rand()%(n-1)+1;
x=modular_exp(a,u,n);
for(int j=0;j<t;j++)
{
y=modular_multi(x,x,n);
if (y==1&&x!=1&&x!=n-1)
return false;
///其中用到定理,如果对模n存在1的非平凡平方根,则n是合数。
///如果一个数x满足方程x^2≡1 (mod n),但x不等于对模n来说1的两个‘平凡’平方根:1或-1,则x是对模n来说1的非平凡平方根
x=y;
}
if (x!=1)///根据费马小定理,若n是素数,有a^(n-1)≡1(mod n).因此n不可能是素数
return false;
}
return true;
}
//************************************************
//pollard_rho 算法进行质因数分解
//************************************************
long long factor[100];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组小标从0开始
long long gcd(long long a,long long b)
{
if(a==0)return 1;//???????
if(a<0) return gcd(-a,b);
while(b)
{
long long t=a%b;
a=b;
b=t;
}
return a;
}
long long Pollard_rho(long long x,long long c)
{
long long i=1,k=2;
long long x0=rand()%x;
long long y=x0;
while(1)
{
i++;
x0=(modular_multi(x0,x0,x)+c)%x;
long long d=gcd(y-x0,x);
if(d!=1&&d!=x) return d;
if(y==x0) return x;
if(i==k){y=x0;k+=k;}
}
}
//对n进行素因子分解
void findfac(long long n)
{
if(miller_rabbin(n))//素数
{
factor[tol++]=n;
return;
}
long long p=n;
while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
findfac(p);
findfac(n/p);
}
求1~n的素数的个数1e11
#define MAXN 100 // pre-calc max n for phi(m, n)
#define MAXM 10010 // pre-calc max m for phi(m, n)
#define MAXP 40000 // max primes counter
#define MAX 400010 // max prime
#define setbit(ar, i) (((ar[(i) >> 6]) |= (1 << (((i) >> 1) & 31))))
#define chkbit(ar, i) (((ar[(i) >> 6]) & (1 << (((i) >> 1) & 31))))
#define isprime(x) (( (x) && ((x)&1) && (!chkbit(ar, (x)))) || ((x) == 2))
long long n;
long long dp[MAXN][MAXM];
unsigned int ar[(MAX >> 6) + 5] = { 0 };
int len = 0, primes[MAXP], counter[MAX];
void Sieve() {
setbit(ar, 0), setbit(ar, 1);
for (int i = 3; (i * i) < MAX; i++, i++) {
if (!chkbit(ar, i)) {
int k = i << 1;
for (int j = (i * i); j < MAX; j += k) setbit(ar, j);
}
}
for (int i = 1; i < MAX; i++) {
counter[i] = counter[i - 1];
if (isprime(i)) primes[len++] = i, counter[i]++;
}
}
void init() {
Sieve();
for (int n = 0; n < MAXN; n++) {
for (int m = 0; m < MAXM; m++) {
if (!n) dp[n][m] = m;
else dp[n][m] = dp[n - 1][m] - dp[n - 1][m / primes[n - 1]];
}
}
}
long long phi(long long m, int n) {
if (n == 0) return m;
if (primes[n - 1] >= m) return 1;
if (m < MAXM && n < MAXN) return dp[n][m];
return phi(m, n - 1) - phi(m / primes[n - 1], n - 1);
}
long long Lehmer(long long m) {
if (m < MAX) return counter[m];
long long res = 0;
int i, a, s, c, y;
s = sqrt(0.9 + m), y = c = cbrt(0.9 + m);
a = counter[y], res = phi(m, a) + a - 1;
for (i = a; primes[i] <= s; i++) res = res - Lehmer(m / primes[i]) + Lehmer(primes[i]) - 1;
return res;
}
计算二次剩余
/*********************************
计算二次剩余模版即x^2 = n(mod)p
*********************************/
typedef long long ll;
ll tmod;
ll mod(ll n,ll p)
{
ll temp = 1;
while(p > 0)
{
if(p & 1) temp = temp * n % tmod;
n = n * n % tmod;
p >>= 1;
}
return temp;
}
ll pows(ll n)
{
ll i;
ll temp = 1;
for(i = 0; i < n; i++)
temp *= 2;
return temp;
}
int main()
{
int n,p,cs;
ll z,q,s,c;
ll r,t,m,b,i;
ll minx;
scanf("%d",&cs);
while(cs--)
{
scanf("%d%d",&n,&p);
//p=2时特判
if(p == 2)
{
if(n % p == 1)printf("1\n");
else printf("No root\n");
continue;
}
//如果无解
tmod = p;
if(mod(n,(p - 1) / 2) != 1)
{
printf("No root\n");
continue;
}
q = p - 1;
s = 0;
while(q % 2 == 0)
{
q /= 2;
s ++;
}
if(s == 1)
{
r = mod(n,(p + 1) / 4);
}
else
{
while(1)
{
z = 1 + rand() % (p - 1);
if(mod(z,(p - 1) / 2) == (p - 1)) break;
}
c = mod(z,q);
r = mod(n,(1 + q) / 2);
t = mod(n,q);
m = s;
while(1)
{
if(t % p == 1) break;
for(i = 1; i < m; i++)
{
if(mod(t,pows(i)) == 1) break;
}
b = mod(c, pows(m - i - 1));
r = r * b % p;
t = t * b * b % p;
c = b * b % p;
m = i;
}
}
r = (r % p + p) % p;
//如果只有一个
if(r == p - r)cout<<r<<endl;
//如果有两个按照升序输出
else
{
minx = min(r,p - r);
cout<<minx<<" "<<(p - minx)<<endl;
}
}
return 0;
}
计算离散对数
/*************************************
*baby_step giant_step
*应用条件n必须为素数
*a^x = b (mod n) ,a,b < n
*求解上式 0<=x < n的解
*************************************/
const int MOD = 76543;
int hs[MOD],head[MOD],Next[MOD],id[MOD],top;
void Insert(int x,int y)
{
int k = x%MOD;
hs[top] = x, id[top] = y, Next[top] = head[k], head[k] = top++;
}
int Find(int x)
{
int k = x%MOD;
for(int i = head[k]; i != -1; i = Next[i])
if(hs[i] == x)
return id[i];
return -1;
}
int BSGS(int a,int b,int n)
{
memset(head,-1,sizeof(head));
top = 1;
if(b == 1)return 0;
int m = sqrt(n*1.0), j;
long long x = 1, p = 1;
for(int i = 0; i < m; ++i, p = p*a%n)Insert(p*b%n,i);
for(long long i = m; ;i += m)
{
if( (j = Find(x = x*p%n)) != -1 )return i-j;//找到了可行解
if(i > n)break;
}
//如果无解
return -1;
}
原根
哪些数有原根?
n=1,2,4,pr,2pr。 n = 1 , 2 , 4 , p r , 2 p r 。 期中p为奇素数,r为任意的正整数。
原根的一些性质:
•一个数n如果有原根,那么有 φ(φ(n)) φ ( φ ( n ) ) 个
•高斯证明了:
•一个数n的全体原根乘积模n余1
•一个数n的全体原根总和模n余μ(n-1)(莫比乌斯函数)
求出n的所有原根,不存在原根输出-1
const int N = 1000000;
bool f[N];
int phi(int x){
if(f[x]) return x-1;
int ans = x;
for(int i=2; i<=x; i++){
if(x%i==0){
while(x%i==0) x/=i;
ans = ans - ans/i;
}
}
if(x>1) ans = ans - ans/x;
return ans;
}
int gcd(int a, int b){
swap(a,b);
int c = a%b;
while(c){
a=b; b=c; c=a%b;
}
return b;
}
int quick_mod(int x, int p, int mod){
long long s = 1;
long long a = x;
while(p){
if(p&1) s = (s*a)%mod;
a = a*a%mod;
p>>=1;
}
return (int)s;
}
vector<int> V;
vector<int> G;
void cal(int x){
G.clear();
if(f[x]) return;
else{
for(int i=2; i*i<=x; i++){
if(x%i==0){
G.push_back(i);
if(i*i!=x) G.push_back(x/i);
}
}
}
}
bool exist(int n){
if(n%2==0) n/=2;
if(f[n]) return 1;
for(int i=3; i*i<=n; i+=2){
if(n%i==0){
while(n%i==0) n/=i;
return n==1;
}
}
return 0;
}
void solve(int n){
if(n==2){
puts("1");
return;
}
if(n==4){
puts("3");
return;
}
if(!exist(n)){
puts("-1");
return;
}
int p = phi(n);
cal(p);
int x = -1;
for(int i=2; i<n; i++){
bool flag = 1;
if(quick_mod(i, p, n)!=1) continue;
for(int j=0; j<G.size(); j++){
if(quick_mod(i, G[j], n)==1){
flag = 0;
break;
}
}
if(flag){
V.resize(1);
V[0] = x = i;
break;
}
}
if(x==-1){
puts("-1");
return;
}
for(int i=2; i<p; i++){
if(gcd(i, p)==1) V.push_back(quick_mod(x, i, n));
}
sort(V.begin(), V.end());
vector<int>::iterator it=unique(V.begin(), V.end());
V.erase(it, V.end());
for(int i=0; i<V.size(); i++){
if(i) putchar(' ');
printf("%d", V[i]);
}
puts("");
}
int main(){
memset(f, 1, sizeof(f));
f[0] = f[1] = 0;
for(int i=2; i<N; i++){
if(f[i]){
for(int j=i<<1; j<N; j+=i) f[j]=0;
}
}
int n;
while(~scanf("%d", &n)) solve(n);
return 0;
}
组合数学
n球m盒分配问题
球可分辨 | 盒子可分辨 | 盒可空 | 方案数 |
---|---|---|---|
是 | 是 | 是 | mn m n |
是 | 是 | 否 | m!S2(n,m) m ! S 2 ( n , m ) |
是 | 否 | 是 | ∑mi=1S2(n,i) ∑ i = 1 m S 2 ( n , i ) |
是 | 否 | 否 | S2(n,m) S 2 ( n , m ) |
否 | 是 | 是 | Cm−1n+m−1 C n + m − 1 m − 1 |
否 | 是 | 否 | Cm−1n−1 C n − 1 m − 1 |
否 | 否 | 是 | 分拆数(n+m,m) |
否 | 否 | 否 | 分拆数(n,m) |
第一类斯特林数:
把一个包含n个元素的集合分成k个环排列的方法数。
s[1][1] = 1;
for(int i = 2; i <= 20; ++i)
for(int j = 1; j <= i; ++j)
{
s[i][j] = s[i-1][j-1] + s[i-1][j]*(i-1);
}
第二类斯特林数:
把一个包含n个元素的集合分成k个非空子集的方法数。
for(int i = 1; i <= 2000; ++i)
{
s[i][1] = 1;
for(int j = 2; j <= i; ++j)
{
s[i][j] = s[i-1][j-1] + j*s[i-1][j];
}
}
Lucas定理:
n,m比较大,p可能为和数。
LL quick_mod(LL a, LL b)
{
LL ans = 1;
a %= p;
while(b)
{
if(b & 1)
{
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
LL C(LL n, LL m)
{
if(m > n) return 0;
LL ans = 1;
for(int i=1; i<=m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans = ans * (a * quick_mod(b, p-2) % p) % p;
}
return ans;
}
LL Lucas(LL n, LL m)
{
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}