数论

1.二项式定理


例题:计算系数

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mo=10007;
int a,b,k,n,m;
ll c[1007][1007];
ll qpow(int a,int b)
{
  ll ans=1,base=a;
  while(b)
  {
   if(b&1)
   ans=ans*base%mo;
   base=base*base%mo;
   b>>=1; 
  }
  return ans;
}
int main()
{ 
 scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
 c[0][0]=1;
 for(int i=1;i<=k;i++)
 c[i][0]=c[i][i]=1;
 for(int i=1;i<=k;i++)
 {
  for(int j=1;j<i;j++)
  {
    c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;
  }
 }
 ll ans=c[k][n]*qpow(a,n)%mo*qpow(b,m)%mo;
 printf("%lld
",ans);
 return 0;
}

2.扩展欧几里得

(ax+by=c)有解当且仅当(d=gcd(a,b)),且(d|c)。方程的通解为(x=frac{c}{d}x_{0}+kfrac{b}{d}),(y=frac{c}{d}y_{0}-kfrac{a}{d}),((kepsilon mathbb{Z})).

其中(x_{0})(y_{0})的求解方法就是扩展欧几里得。

ll exgcd(ll a,ll b,ll &x,ll &y)
{
  if(b==0)
  {
    x=1,y=0;
    return a;
  }
  ll d=exgcd(b,a%b,x,y);
  ll tmp=x;
  x=y;
  y=tmp-y*(a/b);
  return d;//d为gcd
}

例题:同余方程

满分作法:原式可以转化为(ax+by=1),因为保证有解,只能(gcd(a,b)=1).找到第一个正整数解,只需先求解,在加即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
ll a,b;
ll xx,yy;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
  if(b==0)
  {
   x=1,
   y=0;
   return a;
  }
  ll d=exgcd(b,a%b,x,y);
  ll tmp=x;
  x=y;
  y=tmp-y*(a/b);
  return d;
}
int main()
{
 scanf("%lld%lld",&a,&b);//由题gcd(a,b)=1,否则无解 
 exgcd(a,b,xx,yy);
 while(xx<=0)
 xx+=b;
 printf("%lld
",xx);
 return 0;	
}

3.乘法逆元(也就是在取模意义下的除数)

1.方法一:扩展欧几里得求逆元(适用所有)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
ll a,p,xx,yy;
void exgcd(ll a,ll b,ll &x,ll &y)
{
 if(b==0)
 {
  x=1;
  y=0;
  return;
 }
 exgcd(b,a%b,x,y);
 ll tmp=x;
 x=y;
 y=tmp-y*(a/b);
}
int main()
{
 scanf("%lld%lld",&a,&p);
 exgcd(a,p,xx,yy);
 xx=(xx%p+p)%p;//有可能是负数,所以要这样取模
 printf("%lld
",xx);
 return 0;	
}

2.快速幂(两数互质)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
ll a,p;
ll qpow(ll a,ll b)
{
  ll ans=1,base=a;
  while(b){
   if(b&1)
   ans*=base,base%=p;
   base*=base,base%=p;
   b>>=1;
 }
 return ans;
}

int main()
{
  scanf("%lld%lld",&a,&p);
  printf("%lld
",qpow(a,p-2)%p);
  return 0; 
}

3.求连续数(1,2,3,4,5.....)的逆元

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxm=3e6+7;
ll p;
int n;
ll inv[maxm];
int main()
{
 scanf("%d%lld",&n,&p);
 inv[1]=1;
 printf("1
");
 for(int i=2;i<=n;i++)
 {
   inv[i]=(p-p/i)*inv[p%i]%p;//-p/i是负数所以+p 
   printf("%lld
",inv[i]);	
 } 
 return 0;	
} 

4.一个序列的逆元.

例题:乘法逆元2

我们设数组(a)的各项是:

(a_1,a_2,a_3......a_n)

则其逆元可表示为:

(frac{1}{a_1},frac{1}{a_2},frac{1}{a_3},......frac{1}{a_n})

我们取一个数列(s),表示:

(s_i = a_1 * a_2 * ...... * a_i)

其递推式为:

(s_i = s_{i-1}*a_i)

则:

(frac{1}{s_i} = frac{1}{a_1 * a_2 * ...... * a_i})

易得其递推式为:

(frac{1}{s_{i-1}} = frac{1}{s_{i}}*a_i)

于是(frac{1}{a_i})可以表示为

(frac{1}{a_i}=s_{i-1}*frac{1}{s_{i}})

其中(a_i)是已知的,(s_i)是线性递推的,而求出(frac{1}{s_n})后,可以再次线性递推出(frac{1}{s_i})。最后求出(frac{1}{a_i})

最后用秦九韶优化即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxm=5e6+7;
int n;
inline int read()
{
    char ch=getchar();int x=0,f=1;
    while(ch<'0' || ch>'9') {
       if(ch=='-') f=-1;
      	  ch=getchar();
    }
    while(ch<='9' && ch>='0') {
       x=x*10+ch-'0';
       ch=getchar();
    }
    return x*f;
}
inline ll readl() {
    char ch = getchar();
    ll x = 0, f = 1;
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
ll p,k,a[maxm];
ll s[maxm];
ll inv[maxm];
ll ans=0;
ll qpow(ll a,ll b)
{
  ll ans=1,base=a;
  while(b){
   if(b&1)
   ans=ans*base,ans%=p;
   base=base*base,base%=p;
   b>>=1;
 }
 return ans;
}
int main()
{
 n=read();
 p=readl();
 k=readl();
 s[0]=1;
 for(int i=1;i<=n;i++)
 a[i]=readl();
 for(int i=1;i<=n;i++)
 s[i]=a[i]*s[i-1]%p;
 inv[n]=qpow(s[n],p-2);
 for(int i=n-1;i>=1;i--)
 inv[i]=inv[i+1]*a[i+1]%p;
 for(int i=n;i>=1;i--)
 {
  ll node=inv[i]*s[i-1]%p;
  ans=((ans+node)%p*k%p);
 }
 printf("%lld
",ans);
 return 0;
}

5.组合数取模(Lucas定理)

(Lucas_{m}^n equiv Lucas_{m/p}^{n/p} * C_{n mod p}^{m mod p} pmod p)

例题:卢卡斯定理

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxm=2e5+7;
ll n,m,p;
int t;
ll inv[maxm];
ll qpow(ll a,ll b)
{
 ll ans=1,base=a;
 while(b)
 {
   if(b&1) ans=base*ans%p;
   base=base*base%p;
   b>>=1; 
 }	
 return ans;
}
ll C(ll n,ll m)
{
  if(n<m) return 0;
  return inv[n]*qpow(inv[n-m],p-2)%p*qpow(inv[m],p-2)%p;	
}
ll lucas(ll n,ll m)
{
  if(n<m) return 0;
  if(n==0) return 1;
  return C(n%p,m%p)*lucas(n/p,m/p)%p;
} 
int main()
{
 scanf("%d",&t);
 while(t--)
 {
  scanf("%lld%lld%lld",&n,&m,&p);
  inv[0]=1;
  for(int i=1;i<=n+m;i++)
  inv[i]=inv[i-1]*i%p;
  printf("%lld
",lucas(n+m,m));	
 }
 return 0;	
}

6.错排问题

递推公式:(d[i]=(i-1)(d[i-1]+d[i-2])),其中(d[0]=1),(d[1]=0);

证明:见此博客

例题:排列计数

满分作法:相当于在(n)个数中选(m)个数,剩下的错排即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxm=1e6+7;
const ll mo=1e9+7;
int t;
int n,m;
ll d[maxm];//错排数
ll inv[maxm];
ll qpow(ll a,ll b)
{
  ll ans=1,base=a;
  while(b){
   if(b&1)
   ans=ans*base%mo;
   base=base*base%mo;
   b>>=1;
 }
 return ans;
}
ll C(ll n,ll m)
{
 if(n<m) return 0;
 return inv[n]*qpow(inv[n-m],mo-2)%mo*qpow(inv[m],mo-2)%mo;	
}
int main()
{
 scanf("%d",&t);
 d[0]=1;//要特殊处理d[0] 
 d[1]=0;
 d[2]=1;
 for(int i=3;i<=maxm-7;i++)
 d[i]=(i-1)*(d[i-1]+d[i-2])%mo;
 inv[0]=1;
 for(int i=1;i<=maxm-7;i++)
 inv[i]=inv[i-1]*i%mo;
 while(t--)
 {
  scanf("%d%d",&n,&m);
  printf("%lld
",C(n,m)*d[n-m]%mo);
 }
 return 0;	
}

7.卡特兰数

1.令(h(0)=1,h(1)=1)(catalan)数满足递推式。(h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2))

2.(h(n)=C(2n,n)/(n+1))

原文地址:https://www.cnblogs.com/lihan123/p/11839716.html