数论--第一篇


数学知识

** 数论里面好多的题多需要用long long -- 

----------------------------------------------------------------------------------------------------------------

快速幂--快速求出a的k次方模p

typedef long long ll;

ll qmi()
{
  ll res = 1;//注意这里必须是res = 1;
  
  while(k)
  {
    if(k&1) res = res*a%p;
  
    k>>=1;
    
    a = a*a%p;
  }
  
  return res;
}

快速幂 -- 求逆元

费马小定理:


----------------------------------------------------------------------------------------------------------------

gcd
 
int gcd(int a,int b)
{
  return b?gcd(b,a%b):a;
}

----------------------------------------------------------------------------------------------------------------

前置知识:

        裴蜀定理:对于任意正整数a,b,那么一定存在非零整数x,y,使得ax+by = (a,b)/a和b的最大公约数;

扩展gcd

int exgcd(int a,int b,int &x,int &y)
{
  if(!b)
  {
    x = 1,y = 0;
    return a;
  }
  
  int d = exgcd(b,a%b,y,x);
  
  y -= a/b*x;
  
  return d;//返回的是a,b的最大公约数,同时也是一组x,y的解;
}

典型应用 : 线性同余方程 -- a*x == b(modm) ---> a*x-m*y == (a,-m)  判断b能够整除(a,-m);

----------------------------------------------------------------------------------------------------------------

质因数

质数--在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者叫素数;

试除法判定一个质数 

bool is_prime(int n)
{
  if(n<2) return false;
  
  for(int i = 2;i<=n/i;++i)//该处的n/i为最优写法;
  {
    if(n%i == 0) return false;
  }
  
  return true;
}

分解质因数--n中最多只包含一个大于根号n的质数,如果N中出现两个大于根号n的质因子,相乘必定大于n
                                                                                              --见acwing867.
void divided(int n)
{
  for(int i = 2;i<=n/i;++i)
  {
    if(n%i == 0)//i一定是质数--n一定是i的倍数,并且n当中不包含任何2~i-1之间的倍数,所以i当中也不包含任何2~i-1之间
                //的质因子;
    {
      int res = 0;
      while(n%i == 0)
      {
        res++;
        n/=i;
      }
      
      cout<<i<<" "<<res<<endl;
    }
  }
  
  if(n>1) cout<<n<<" "<<1<<endl;
}

筛选质因数 -- 对于一个合数x,假设p[j]是x的最小质因子,当i枚举到x/p[j]的时候,这个合数被筛

      **用最小质因子筛素数 --所以i%primes[j] == 0 的目的是为了防止重复筛选合数;

int primes[N],cnt;
bool st[N];

void get_primes(int n)
{
  for(int i = 2;i<=n;++i)
  {
    if(!st[i]) primes[cnt++] = i;
    
    for(int j = 0;primes[j]<=n/i;++j)
    {
      st[primes[j]*i] = true;
      if(i%primes[j] == 0) break;
    }
  }

  1.i%p[j] == 0--primes[j]一定是i的最小质因子;
  
  2.i%p[j]!= 0 --p[j]也一定是p[j]*i的最小质因子;

----------------------------------------------------------------------------------------------------------------

求组合数

1--

1<=b<=a<2000 递推;

const int N = 2010,mod = 1e9+7;

int c[N][N];

void init()
{
  for(int i = 0;i<N;++i)
  {
    for(int j = 0;j<=i;++j)
    {
      if(!j) c[i][j] = 1;
      else c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
    }
  }
  
}

2--
 
1<=b<=a<=100000 预处理--阶乘思想;

const int N = 100010,mod = 1e9+7;

int fact[N],infact[N];//分别存储分子阶乘和逆元阶乘;

typedef long long ll;

ll qmi(ll a,ll b,ll k)//用快速幂计算逆元--费马小定理--k一定是质数;
{
  ll res = 1;
  
  while(b)
  {
    if(b&1) res = (ll)res*a%k;
    
    b>>=1;
    
    a = (ll)a*a%k;
  }
  
  return res;
}

void init()//预处理;
{
  fact[0] = infact[0] = 1;
  
  for(int i = 1;i<N;++i)
  {
    fact[i] = (ll)fact[i-1]*i%mod;
    infact[i] = (ll)infact[i-1]*qmi(i,mod-2,mod)%mod;
  }
}
  
3--

1<=b<=a<=10(18次) -- 卢卡斯定理--lucas

typedef long long ll;

int n,p;//p为模的值;

ll qmi(ll a,ll b)//快速幂求逆元;
{
  ll res = 1;
  
  while(b)
  {
    if(b&1) res = res*a%p;
    
    b>>=1;
    
    a = a*a%p;
  }
  
  return res;
}


ll C(ll a,ll b)//阶乘思想求组合数
{
  ll  res = 1;
  
  for(int i = 1,j = a;i<=b;++i,--j)//当i枚举到b的时候,j也从a枚举到a-b+1了;
  {
    res = res*j%p;
    
    res = res*qmi(i,p-2)%p;
  }
  
  return res;
}


ll lucas(ll a,ll b)//卢卡斯算法求最终的结果;
{
  if(a<p && b<p) return C(a,b);
  else return (ll)C(a%p,b%p)*lucas(a/p,b/p)%p;
}

----------------------------------------------------------------------------------------------------------------
离散化-- 范围广(10^9)--值稀疏

查找--
int find(int x)
{
  int l = 0,r = alls.size()-1;
  
  while(l<r)
  {
    int mid = l+r>>1;
    
    if(alls[mid]>=x) r = mid;
    else l = mid+1;
  }
  
  return r+1;
}

存储--

sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());

----------------------------------------------------------------------------------------------------------------

约数--

试除法求约数

vector<int> get_privisors(int x)
{
  vector<int> res;
  
  for(int i = 1;i<=x/i;++i)//这里是i== 1,约数包括1和它本身;
  {
    if(x%i == 0)
    {
      res.push_back(i);
      
      if( i != x/i) res.push_back(x/i);
    }
  }
  
  sort(res.begin(),res.end());
  
  return res;
}

约数个数--来源是算术分解定理(k0+1)+(k1+1)+...+(k&+1)因此i从2开始遍历;

unordered_map<int,int> primes;//用哈希记录;

for(int i = 2;i<=x/i;++i)
    {
      while(x%i == 0)
      {
        x /= i;
        
        primes[i]++;
      }
    }
    
    if(x>1) primes[x]++;


  ll res = 1;
  
  for(auto p : primes) res = res*(p.second+1)%mod;

约数之和-- 

unordered_map<int,int> hash;
  
  while(n--)
  {
    int x;
    cin>>x;
    
    for(int i = 2;i<=x/i;++i)
    {
      while(x%i == 0)
      {
        x = x/i;
        
        hash[i]++;
      }
    }
    if(x>1) hash[x]++;
  }
  
  ll res = 1;
  
  for(auto p:hash)
  {
    ll a = p.first,b = p.second;
    
    ll t = 1;
    
    while(b--) t = (t*a+1)%mod;
    
    res = t%mod*res%mod;
  }

----------------------------------------------------------------------------------------------------------------

欧拉函数--

          求1~n中与n互质的数的个数;

while(n--)
  {
    int a;
    cin>>a;
    
    int res = a;
    for(int i = 2;i<=a/i;++i)
    {
      if(a%i == 0)
      {
        res = res/i*(i-1);
        while (a%i == 0) a = a/i;
      }
    }
    
    if(a>1) res = res/a*(a-1);
    
    
    cout<<res<<endl;
  }

线性筛法求欧拉函数 -- 求1~n中所有数的欧拉函数的和;

void enlers(int n)
{
  enler[1] = 1;
  
  for(int i = 2;i<=n;++i)
  {
    if(!st[i])
    {
     primes[cnt++] = i;
     enler[i] = i-1;  
    }
    
    for(int j = 0;primes[j]<=n/i;++j)
    {
      int t = primes[j]*i;
      
      st[t] = true;
      
      if(i%primes[j] == 0)
      {
        enler[t] = enler[i]*primes[j];
        break;
      }
      
      enler[t] = enler[i]*(primes[j]-1);
    }
  }
}

----------------------------------------------------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值