[SinGuLaRiTy] 复习模板-数学

【SinGuLaRiTy-1047】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

质因数分解

void solve(int n)
{
    while(n%2==0)
    {
        printf("%d*",2);
        n/=2;
    }
    for(int i=3;i<=sqrt(n);i+=2)
    {
        if(n%i==0)
        {
            n/=i;
            printf("%d*",i);
            i-=2;
        }
    }
    printf("%d\n",n);
}

欧拉线性筛素数

#define MAXN 100005
#define MAXL 1299710

int prime[MAXN];
int check[MAXL];

int tot=0;

void get_prime()
{
    for(int i=2;i<MAXL;i++)
    {
          if(!check[i])
            prime[tot++]=i;
          for(int j=0;j<tot;j++)
          {
            if(i*prime[j]>MAXL)
                break;
            check[i*prime[j]]=1;
            if(i%prime[j]==0)
                 break;
          }
    }
}

筛法求欧拉函数(线性)

#include<iostream>
#include<cstdio>
#define N 40000
using namespace std;
int n;
int phi[N+10],prime[N+10],tot,ans;
bool mark[N+10];
void getphi()
{
   int i,j;
   phi[1]=1;
   for(i=2;i<=N;i++)//相当于分解质因式的逆过程
   {
       if(!mark[i])
       {
           prime[++tot]=i;//筛素数的时候首先会判断i是否是素数。
           phi[i]=i-1;//当 i 是素数时 phi[i]=i-1
       }
       for(j=1;j<=tot;j++)
       {
          if(i*prime[j]>N)                break;
          mark[i*prime[j]]=1;//确定i*prime[j]不是素数
          if(i%prime[j]==0)//接着我们会看prime[j]是否是i的约数
          {
              phi[i*prime[j]]=phi[i]*prime[j];              break;
          }
          else                phi[i*prime[j]]=phi[i]*(prime[j]-1);//其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性
       }
   }
}  

Miller-Rabbin素数判定法

#include<cstdlib>
#include<ctime>
#include<cstdio>

using namespace std;

const int count=10;

int modular_exp(int a,int m,int n)
{
    if(m==0)
        return 1;
    if(m==1)
        return (a%n);
    long long w=modular_exp(a,m/2,n);
    w=w*w%n;
    if(m&1)
        w=w*a%n;
    return w;
} 

bool Miller_Rabin(int n)
{
    if(n==2)
        return true;
    for(int i=0;i<count;i++)
    {
        int a=rand()%(n-2)+2;
        if(modular_exp(a,n,n)!=a)
            return false;
    }
    return true;
}

int main()
{
    srand(time(NULL));
    int n;
    scanf("%d",&n);
    if(Miller_Rabin(n))
        printf("Probably a prime.");
    else
        printf("A composite.");
    printf("\n");
    return 0;
}

倍增求快速幂

typedef long long ll;

ll fast_power(ll a,ll b,ll c)//求a^b%c
{
    ll ans=1;
    while(b)
    {
        if (b&1)
            ans=ans*a%c;
        else
            ans%=c;
        b>>=1;
        a=a*a%c;
    }
    return ans;
}

大数乘法取幂

typedef long long ll

ll qmul(ll x,ll y,ll mod)// 乘法防止溢出,如果p*p不爆LL的话可以直接乘;O(1)乘法或者转化成二进制加法(快速加)
{
    ll ret=0;
    while(y)
    {
        if(y&1)
            ret=(ret+x)%mod;
        x=x*2%mod;
        y>>=1;
    }
    return ret;
}

ll qpow(ll a,ll n,ll mod)
{
    ll ret=1;
    while(n)
    {
        if(n&1)
            ret=qmul(ret,a,mod);
        a=qmul(a,a,mod);
        n>>=1;
    }
    return ret;
}

GCD & LCM

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

至于LCM=a*b/GCD,可能会因为a*b过大而爆掉,于是推荐使用LCM=a/GCD*b

Exgcd

求ax=b (mod m) ax+my=b 如果r=gcd(a,m)且b%r==0,则同余方程有解,其最小解为x*(b/r);
ax+by=c 如r=gcd(a,b),则存在x,y,使xa+yb=r;当x+=b,y-=a后仍然成立        
因为xa+yb+ab-ab=r;则(x+b)a+(y-a)b=r

int exgcd(int a,int b,int &x,int &y)
{
     if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
     int r=exgcd(b,a%b,y,x);
     y-=x*(a/b);
     return r;
}

中国剩余定理

设正整数m1,m2,m3...mk两两互素,则同余方程组

有整数解。并且在模M=m1·m2·...·mk下的解是唯一的,解为

其中,而的逆元。

void extend_Euclid(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    extend_Euclid(b,a%b,x,y);
    int tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}  

int CRT(int a[],int m[],int n)
{
    int M=1;
    int ans=0;
    for(int i=1;i<=n;i++)
        M*=m[i];
    for(int i=1;i<=n;i++)
    {
        int x,y;
        int Mi=M/m[i];
        extend_Euclid(Mi,m[i],x,y);
        ans=(ans+Mi*x*a[i])%M;
    }
    if(ans<0)
        ans+=M;
    return ans;
}  

Catalan数

f[1]=1;
for(int i=2;i<=n;i++)
    f[i]=f[i-1]*(4*i-2)/(i+1);

Catalan数有许多神奇的性质,这是一篇总结的比较精炼的文章。《Catalan数——卡特兰数》

康托展开式

int  fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]
// 康托展开-> 表示数字a是 a的全排列中从小到大排,排第几
// n表示1~n个数  a数组表示数字。
int kangtuo(int n,char a[])
{
    int i,j,t,sum;
    sum=0;
    for( i=0;i<n;++i)
    {
        t=0;
        for(j=i+1;j<n;++j)
            if(a[i]>a[j])
                ++t;
        sum+=t*fac[n-i-1];
    }
    return sum+1;
}  

有啥用?它可以应用于哈希表中空间压缩。在码某些搜索题时,将VIS数组量压缩。比如:八数码魔板

求乘法逆元

exgcd,老熟人了。

/*
求解ax+by=gcd(a,b),亦即ax≡1(mod b)。函数返回值是a,b的最大公约数,而x即a的逆元。
注意a, b不能写反了。
*/
int x,y;
int extgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int gcd=exgcd(b,a%b, x, y);
    int tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return gcd;
}  

N的全排列

#include<iostream>

using namespace std;

void swap(int &a,int &b)
{
    int temp=a;
    a=b;
    b=temp;
}

void pai_xu(int a[],int m,int n)
{
    if(m==n)
        {
        for(int i=1;i<=n;i++)
            cout<<a[i];
        cout<<endl;
    }
    else
        for(int i=m;i<=n;i++)
                {
            swap(a[i],a[m]);
            pai_xu(a,m+1,n);
            swap(a[i],a[m]);
        }
}

int main()
{
    int n,m=1,a[10];
    cin>>n;
    for(int i=1;i<=n;i++)
        a[i]=i;
    pai_xu(a,m,n);
    return 0;
}

N的R排列

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>

using namespace std;

bool b[21]={0};
int total=0,a[21]={0};

bool pd(int x,int y)
{
     int k=2,i=x+y;
     while(k<=sqrt(i)&&i%k!=0)
         k++;
     if(k>sqrt(i))
         return 1;
     else
         return 0;
}

int print()
{
    total++;
    cout<<total<<" "<<endl;
    for(int i=1;i<=20;i++)
        cout<<a[i]<<" ";
    cout<<endl;
}

int search(int t)
{
    for(int i=1;i<=20;i++)
        if(pd(a[t-1],i)&&(!b[i]))
        {
            a[t]=i;
            b[i]=1;
            if(t==20)
            {
                if(pd(a[20],a[1]))
                    print();
            }
            else
                search(t+1);
            b[i]=0;
        }
}

int main()
{
    search(1);
    cout<<total<<endl;
    return 0;
}

有重复元素的全排列

void fun(int n,char chars[],int flag) {
    if(flag==n-1) {
        for(int i=0;i<n;i++) cout<<chars[i];
        cout<<endl;
        return;
    }
    for(int i=flag;i<n;i++) {
        if(chars[i]!=chars[flag]||i==flag){//若两个元素不相等或者两个元素的下标相同的时候才调用
            swap(chars[i],chars[flag]);
            fun(n,chars,flag+1);
            swap(chars[i],chars[flag]);
        }
    }
}  

第一类Stirling数

定理:第一类Stirling数s(p,k)计数的是把p个对象排成k个非空循环排列的方法数。

证明:把上述定理叙述中的循环排列叫做圆圈。递推公式为:

s(p,p)=1 (p>=0)    有p个人和p个圆圈,每个圆圈就只有一个人

s(p,0)=0 (p>=1)    如果至少有1个人,那么任何的安排都至少包含一个圆圈

s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1)

设人被标上1,2,.....p。将这p个人排成k个圆圈有两种情况。第一种排法是在一个圆圈里只有标号为p的人自己,排法有s(p-1,k-1)个。第二种排法中,p至少和另一个人在一

个圆圈里。这些排法可以通过把1,2....p-1排成k个圆圈再把p放在1,2....p-1任何一人的左边得到,因此第二种类型的排法共有(p-1)*s(p-1,k)种排法。

在证明中我们所做的就是把{1,2,...,p}划分到k个非空且不可区分的盒子,然后将每个盒子中的元素排成一个循环排列。

long long s[maxn][maxn];//存放要求的第一类Stirling数
const long long mod=1e9+7;//取模  

void init()//预处理
{
    memset(s,0,sizeof(s));
    s[1][1]=1;
    for(int i=2;i<=maxn-1;i++)
        for(int j=1;j<=i;j++)
    {
        s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];
        if(s[i][j]>=mod)
            s[i][j]%=mod;
    }
}  

第二类Stirling数

定理:第二类Stirling数S(p,k)计数的是把p元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。

证明:元素在拿些盒子并不重要,唯一重要的是各个盒子里装的是什么,而不管哪个盒子装了什么。

递推公式有:S(p,p)=1 (p>=0)         S(p,0)=0  (p>=1)         S(p,k)=k*S(p-1,k)+S(p-1,k-1)   (1<=k<=p-1)。考虑将前p个正整数,1,2,.....p的集合作为要被划分的集合,把

{1,2,.....p}分到k个非空且不可区分的盒子的划分有两种情况:

(1)那些使得p自己单独在一个盒子的划分,存在有S(p-1,k-1)种划分个数

(2)那些使得p不单独自己在一个盒子的划分,存在有 k*S(p-1,k)种划分个数

考虑第二种情况,p不单独自己在一个盒子,也就是p和其他元素在一个集合里面,也就是说在没有放p之前,有p-1个元素已经分到了k个非空且不可区分的盒子里面(划

分个数为S(p-1,k),那么现在问题是把p放在哪个盒子里面呢,有k种选择,所以存在有k*S(p-1,k)。

long long s[maxn][maxn];//存放要求的Stirling数
const long long mod=1e9+7;//取模  

void init()//预处理
{
    memset(s,0,sizeof(s));
    s[1][1]=1;
    for(int i=2;i<=maxn-1;i++)
        for(int j=1;j<=i;j++)
    {
        s[i][j]=s[i-1][j-1]+j*s[i-1][j];
        if(s[i][j]>=mod)
            s[i][j]%=mod;
    }
}  

注意:要用long long类型,当元素个数>20,就超int类型了。

扩展:k! *S(p,k) 计数的是把p元素集合划分到k个可区分的盒子里且没有空盒子的划分个数。

组合数

//组合数打表模板,适用于N<=3000
//c[i][j]表示从i个中选j个的选法。
long long C[N][N];
void get_C(int maxn)
{
    C[0][0] = 1;
    for(int i=1;i<=maxn;i++)
    {
        C[i][0] = 1;
        for(int j=1;j<=i;j++)
            C[i][j] = C[i-1][j]+C[i-1][j-1];
        //C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
    }
}

LUCAS定理

long long F[100010];
void init(long long p)
{
    F[0] = 1;
    for(int i = 1;i <= p;i++)
        F[i] = F[i-1]*i % (1000000007);
}
long long inv(long long a,long long m)
{
    if(a == 1)return 1;
    return inv(m%a,m)*(m-m/a)%m;
}
long long Lucas(long long n,long long m,long long p)
{
    long long ans = 1;
    while(n&&m)
    {
        long long a = n%p;
        long long b = m%p;
        if(a < b)return 0;
        ans = ans*F[a]%p*inv(F[b]*F[a-b]%p,p)%p;
        n /= p;
        m /= p;
    }
    return ans;
}  

容斥原理

这个怎么打板呢?难道搞一道容斥的题?还是偷偷懒附个链接吧:

《算法中的容斥原理》

Time: 2017-11-05

时间: 2024-10-11 08:06:07

[SinGuLaRiTy] 复习模板-数学的相关文章

模板—数学—矩阵树定理

模板—数学—矩阵树定理 Code: #include <cstdio> #include <algorithm> using namespace std; #define N 1010 #define mod 1000000000 long long ans,squ[N][N];int n,m; long long calc(int n) { long long tmp=1; for(int i=1;i<n;i++) { int j; for(j=i;j<n;j++)

模板—数学—Lucas

模板—数学—Lucas Code: #include <cstdio> #include <algorithm> using namespace std; #define N 100010 int n,m,p,inv[N],powq[N]; int lucas(int n,int m) { if(n<m) return 0; if(n<=p&&m<=p) return 1ll*powq[n]*inv[m]%p*inv[n-m]%p; return

noip2017考前基础复习——数论数学

·最大公约数 gcd 辗转相除法  gcd(a,b)=gcd(b,a%b) 1 int gcd(int x,int y){ 2 if(y==0) return x; 3 return gcd(y,x%y); 4 } 效率O(logn) ·最小公倍数 lcm 可由最大公约数推来 lcm(a,b)=a*b/gcd(a,b) 1 int lcm(int x,int y){ 2 int p=gcd(x,y); 3 return a*b/p; 4 } 效率O(logn) ·扩展欧几里得 extgcd 求a

noip专题复习之数学(6)——置换及其应用

1.基本概念: 简单来说,置换就是把n个元素做一个全排列.比如1,2,3,4分别变成3,1,2,4,或者分别变成4,3,2,1.一般地,1变a1,2变a2,...的置换记为: \[ \left( \begin{matrix} 1 & 2\cdots & n\a_1 & a_2\cdots & a_n\\end{matrix} \right) \] 置换实际上就是一一映射, ? (1)可以用一个数组f={a1,a2,...,an}来表示1~n的一个置换,其中f[i]表示元素i

noip专题复习之数学(5)——概率与数学期望

1.全概率公式: 将样本分成若干个不相交的部分B1,B2,...,Bn,则P(A)=P(A|B1)P(B1)+P(A|B2) P(B2)+...+P(A|Bn)*P(Bn).(P(A|B)是指在B事件发生的条件下,事件A发生的概率. 使用全概率公式的关键是"划分样本空间",只有把所有可能不重不漏地进行分类,并算出每个分类下事件发生的概率,才能得出该事件发生的总概率. 2.数学期望: 简单地说,随机变量X的数学期望EX就是所有可能值按照概率加权的和. 比如一个随机变量有1/2的概率为1,

模板 - 数学 - 组合数

线性预处理组合数,一般来说只是需要用一个组合数,当MOD大于n和m的时候可以这样求,否则应该用扩展Lucas定理. const ll MOD = 1e9 + 7; const int MAXN = 1e6; ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5]; void init_C(int n) { inv[1] = 1; for(int i = 2; i <= n; i++) inv[i] = inv[MOD % i] * (MOD - MOD

noip复习模板

我只会这么多 tarjan:codevs 1332 void tarjan(int u) { dfn[u]=low[u]=Time++; s.push(u); for(int i=head[u];~i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) tarjan(v); else if(dfn[v]!=-1) low[u]=Min(low[u],low[v]); } if(dfn[u]==dfn[v]) { while(!s.empty()) { int x=s.top

[模板] 数学

数论 exgcd 用途 解不定方程 $ ax+by = c $ 代码 ll exgcd(ll a,ll b,ll &x,ll &y){ if(b==0){x=1,y=0;return a;} ll tmp=exgcd(b,a%b,y,x); y-=a/b*x; return tmp; } int main(){ ll a,b,c; ll m,x,y; m=exgcd(a,b,x,y); if(c%m!=0)cout<<"No\n"; //无解 else{ c

模板 - 数学 - 博弈论

1.要从必胜或必败的局面反推 2.SG函数 只要当前状态可以转移到的状态中有一个是败态,那么当前状态就是胜态.胜态为N. 如果当前状态可以转移到的所有状态都是胜态,那么当前状态就是败态.败态为P. sg函数为每个状态赋一个自然数的值,这个值为除这个状态的后继外的最小自然数.首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数.例如mex{0,1,2,4}=3.mex{2,3,5}=0.mex{}=0. 从图的汇点开始反推,可知汇点