数论总结

数论总结

欧拉定理

aφ(n)1(modn)

φ(n) 为欧拉函数

使用条件为gcd(a,n) = 1。

欧拉降幂公式:

abab%φ(n)+φ(n)(modn)(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=124pr2pr 期中p为奇素数,r为任意的正整数。

原根的一些性质:

•一个数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!S2(n,m)
mi=1S2(n,i)
S2(n,m)
Cm1n+m1
Cm1n1
分拆数(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;    
}   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值