你好 数论(未完待续)

据说每年noip都会考数论的题?!

蒟蒻表示智商不够,被数论完虐。。。。。

所以我就只能在这里整理整理各种数论的板子了。。。。

      一.素数

http://www.cnblogs.com/mycapple/archive/2012/08/07/2626898.html

1.欧拉筛(线性筛)

int Euler_sieve()
{
    memset(not_prime,0,sizeof(not_prime));
    for(int i=2;i<=n;i++)
    {
        if(!not_prime[i]) prime[++tot]=i;
        for(int j=1;j<=tot;j++) 
        {
            if(i*prime[j]>N) break;
            not_prime[i*prime[j]]=1;
            if(i%prime[j]==0) break;
         } 
    }
}//若 not_prime[i]为0则为素数     注意:not_prime[1]=1;

2.简单的素数判定

int pd(int x)
{
    for(int i=2;i*i<=x;i++)
     if(x%i==0) return false;
    return  true;
 } 

        二.欧几里得+扩展欧几里得

讲解:http://blog.csdn.net/zhjchengfeng5/article/details/7786595 

1.欧几里得

int gcd(int a,int b)
{
    if(b==0) return a;
    else return gcd(b,a%b);
}

2.扩展欧几里得

①求乘法逆元

再求乘法逆元的时候可以用一个更简便的方法,当这个数与他的模数互质时,它的乘法逆元为这个数的mod-2次方。(用快速幂)

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,x,y),tmp;
    tmp=x,x=y,y=tmp-a/b*y;
    return r;
}
int main()
{
    a=read(),b=read();
    gcd=exgcd(a,b,x,y);
    if(gcd!=1) printf("不存在");
    else {while(x<=0) x+=b/gcd; printf("%d",x);} 
}

②小扩展:除法取模运算

(a/b)mod p=? 
定义 c为b在mod p意义下的逆元
=a*c mod p = (a mod p * c mod p)mod p

③求解同余方程

拓展欧得用于求解形如 ax + by = gcd(a,b) 的不定方程的一组解.   该算法保证求出的 x,y 满⾜足|x| + |y| 最小

 ax三c(mod b)——>   ax+by=c——>我们可以知道c=gcd*c/gcd,那么原式又可以变成ax/(c/gcd)+by/(c/gcd)=c/(c/gcd)我们令x1=x/(c/gcd),y1=y/(c/gcd),——>x=x1*(c/gcd) 那么原式又可以变成ax1+by1=gcd;  最终x=x1*c/gcd

④.两个人的追及相遇问题:设我们用了x时间,一共相差了y圈,总长度为s,A的起点为s1,速度为v1,B的起点为s2,速度为v2,我们可以列出这样一个式子:s1+v1*x-(s2+v2*x)=y*s——>(v1-v2)*x-s*y=s2-s1.我们设a=v1-v2,b=-s,c=s2-s1,那么上式就可以化成ax+by=c我们接着进行化简——>a/c*x+b/c*y=1,设a1=a/c,b1=b/c,c1=c/c.原式就变成了a1*x+b1*y=1,然后就是裸的扩展欧几里得了

      三.欧拉函数

http://blog.csdn.net/sentimental_dog/article/details/52002608

求1~n中有几个数与n互质

int get_phi(int x)
{
    int sum=x;
    if(x%2==0)
    {
        while(x%2==0) x/=2;
        sum/=2;
    }
    for(int i=3;i*i<=x;i+=2)
    {
        if(x%i==0)
        {
            while(x%i==0) x/=i;
            sum=sum/i*(i-1);
        }
    }
    if(x>1) sum=sum/x*(x-1);
    return sum;
}

小规律:(需要用欧拉函数的情况)

1.对于方阵的问题,我们如果要求一个人站在一个位置时事业所能看到的人的个数可以将其转换成欧拉函数。我们将其所在的位置设为(0,0),那么如果存在一个点(x,y),且有gcd(x,y)=k(k!=1),那么点(x/k,y/k)一定会将(x,y)挡住。而如果k=1,那么点(x,y)就一定会被看到。   这样就会想到这不是欧几里得吗??怎么跟欧拉函数扯上关系了???       某位大佬跟我说你用欧几里得吧,T死你。我们把这个题的式子列出来

n   n                                                          n   i    

∑  ∑  [gcd(i,j) = 1] + 2    将以上式子拆成两半等于  2(∑  ∑ [gcd(i,j)=1]))+1

i=1 j=1                    i                                  i=1 j=1 

我们又可以知道 φ(i) =∑ j=1 [gcd(i,j) = 1]   所以就真的变成了裸地欧拉函数

2.求1~n中与n的最大公约数大于m的数的个数

我们考虑gcd这样一个性质gcd(x,y)=m则gcd(x/m,y/m)=1;我们就可以轻易的发现在这个地方的x/m不就是我们要求的第一个式子中的x吗??这样我们就只需要统计这样的x/m的个数不就好了吗?!发现这样会T的很惨。这样我们来找一个不T的方法:一个数的最大公约数是不是一定是她的因子。求每一个数的公约数的因子的时候是不是可以直接枚举到根n??对于我们处理出来的因子是不是有两个来源,一个是本身i,另一个是n/i??

这样我们就可以分两种情况来判断,一是i>m,另一种是n/i大于m,这样我们再求n/i的欧拉函数与n/n/i即i的欧拉函数就好了。

欧拉函数+处理

 for(int i=1;i*i<=n;i++)
        {
            if(n%i==0)
            {
               if(i>=m&&i*i!=n) ans+=get_phi(n/i);
               if(n/i>=m) ans+=get_phi(i);
            }
        } 

 

      四.卡特兰数

http://www.cnblogs.com/z360/p/6561567.html

卡特兰数的应用:

1.给定N个节点,能构成多少种不同的二叉树?     一个有n个结点的二叉树总共有多少种形态

2.在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)。      求一个凸多边形区域划分成三角形区域的方法数

3.矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?

4.在一个圆上,有2*K个不同的结点,我们以这些点为端点,连K条线段,使得每个结点都恰好用一次。在满足这些线段将圆分成最少部分的前提下,请计算有多少种连线的方法    在一个圆上2*K个不同的结点连K条线段,每个结点用一次将圆分成最少部分的前提下有多少种连线的方法 

5.出栈次数      给定1-n问出栈序列的种类数

6.n对括号有多少种匹配方式

7.物理老师和生物老师排队,物理老师前面的生物老师的个数不能超过物理老师,问排队方案数

核心代码:

h[0]=1,h[1]=1;
for(int i=2;i<=n;i++)
 for(int j=1;j<=i;j++)
  h[i]=h[j-1]*h[i-j]+h[i];
ans=h[n];

高精卡特兰数

#define N 110
int n,len,ans,sum,tmp,a[N][N],b[N];
int catelan()
{
    len=1; a[1][0]=b[1]=1;
    for(int i=2;i<110;i++)
    {
        for(int j=0;j<len;j++)
         a[i][j]=a[i-1][j]*(4*i-2);
        sum=0;
        for(int j=0;j<len;j++)
        {
            tmp=sum+a[i][j];
            a[i][j]=tmp%10;
            sum=tmp/10;
        }
        while(sum)
        {
            a[i][len++]=sum%10;
            sum/=10;
        }
        for(int j=len-1;j>=0;j--)
        {
            tmp=sum*10+a[i][j];
            a[i][j]=tmp/(i+1);
            sum=tmp%(i+1);
        }
        while(!a[i][len-1])
         len--;
        b[i]=len;
    }
}

 卡特兰数递推式:f[i][j]=f[i-1][j]+f[i][j-1]

for(int i=0;i<=30;i++)
     f[i][0]=1;
    for(int j=1;j<=30;j++)
     for(int i=j;i<=30;i++)
      f[i][j]=f[i-1][j]+f[i][j-1];

扩展卡特兰数(这个我也不是很清楚,不要被我忽悠了。。。)

1.有n个1和m个-1(n>=m),共n+m个数排成一列,满足对所有0<=k<=n+m的前k个数的部分和Sk > 0的排列数。 问题等价一个格点阵列中,从(0,0)走到(n,m)且不经过对角线x==y的方法数(x > y)。s=f[n][m] f[i][j]=f[i-1][j]+f[i][j-1]

2.有n个1和m个-1(n>=m),共n+m个数排成一列,满足对所有0<=k<=n+m的前k个数的部分和Sk >= 0的排列数。(和问题1不同之处在于此处部分和可以为0,这也是更常见的情况) 问题等价为在一个格点阵列中,从(0,0)点走到(n,m)点且不穿过对角线x==y的方法数(可以走到x==y的点)。        把(n,m)点变换到(n+1,m)点,问题变成了问题1。

           五.中国剩余定理(孙子定理)

http://www.cnblogs.com/z360/p/7341176.html

求解形如的方程组,其中 m i 两两互质.常用于求 m^n (mod p), 其中 n 需要取模而 p 是个质数. 

一直在纠结同余方程无解的情况,后一位大佬跟我说的

 

 一次同余方程组有解的条件
 不是所有的一次同余方程组都是有解的。特别是有些乱写的同余方程组,都没有解。
一次同余方程组有解的条件是:
1、 方程组之间的模M (即除数),要两两互质。即两个模M1、M2的最大公约(M1、M2)=G,G=1
如 X≡3  ( Mod  4 )
X≡5  ( Mod  7 )
(4 ,7 ) =1,有最小解 X=19
2、 如果不互质,但公约数G能整除两个余数R之差。即G|(R1-R2) ,则亦有解。
如 X≡13   ( Mod  14 )
X≡3    ( Mod  6 )
虽然6 与14 不互质,(6 ,14) =2,但由于余数13与3之差为13-3=10,2整除10 ( 2∣10 ),
所以仍然有解。用我的方法,解得最小解  X=27。
3、 如果这两个条件都达不到,则方程组无解。
如 X≡12   ( Mod  14 )
X≡3    ( Mod  6 )
模 (6 ,14) =2,余数之差 12-3=9 ,2不能整除9,所以该同余方程组无解,不必浪费工夫去硬算了。

 

模板:(两两互质)

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

 (两两不互质)这个让我讲我也不会,就只能背过了、、、、、

int crt()
 {
     int a1=a[1],a2,m2,c,d;m1=m[1];
     for(int i=2;i<=n;++i)
     {
         int x=0,y=0;
         a2=a[i],m2=m[i];
         c=a2-a1;
         d=exgcd(m1,m2,x,y);
         int mod=m2/d;
         if(c%d) return -1;
         x=x*c/d;
         x=(x%mod+mod)%mod;
         a1+=m1*x; m1*=mod;
     }
     if(a1==0) a1+=m1;
     return a1;
 }

          六。斐波那契数列

模板

   x=sqrt(5.0);
    ans=(pow(((1+x)/2),n)/x-pow(((1-x)/2),n)/x);

高精斐波那契

int fei(int a,int b,int c)
{
    for(int i=1;i<=max(len[b],len[c]);i++)
    {
        f[a][i]+=f[b][i]+f[c][i];
        if(f[a][i]>9)
        {
            f[a][i+1]=f[a][i]/10;
            f[a][i]=f[a][i]%10;
            len[a]=max(len[a],i+1);
        }
        else len[a]=max(len[a],i);
    }
}
int main()
{
    n=read();
    f[1][1]=1,f[2][1]=2,len[0]=1,len[1]=1;
    for(int i=3;i<=n;i++)
     fei(i,i-1,i-2);
    for(int i=len[n];i>=1;i--)
     printf("%d",f[n][i]);
    return 0;
}

gcd(f[n],f[m])=f[gcd(n,m)]  

求斐波那契第n项,且n比较大时:http://blog.csdn.net/George__Yu/article/details/77249237?locationNum=7&fps=1

矩阵乘法加速斐波那契

 http://www.cnblogs.com/z360/p/7687940.html

struct Node
{
    long long m[3][3];
    Node(){memset(m,0,sizeof(m));}
}mb,ans;
int GCD(int a,int b)
{
    if(b==0) return a;
    return GCD(b,a%b);
}
Node operator*(Node a,Node b)
{
    Node c;
    for(int i=1;i<=2;i++)
     for(int j=1;j<=2;j++)
      for(int k=1;k<=2;k++)
       c.m[i][j]=(c.m[i][j]%mod+a.m[i][k]*b.m[k][j]%mod)%mod;
    return c;
}
int main()
{
    n=read();n--;
    mb.m[1][1]=mb.m[1][2]=mb.m[2][1]=1;
    ans.m[1][1]=ans.m[2][2]=1;
    while(n)
    {
        if(1&n) ans=ans*mb;
        mb=mb*mb;n>>=1;
    }
    cout<<ans.m[1][1];
    return 0;
}

 

 

 

 

                七:排列组合

http://jingyan.baidu.com/article/63acb44ac60d4e61fcc17e2e.html

排列:满足m<=n,从n个元素中取出m个元素,问这m个元素的排列方案    组合:从n个元素中取出m个元素形成一个组合,问组合数

直接上公式:          

不过据学长说,如果%p的话 要是p为素数 就可以搞一下逆元(欧拉函数 gcd都可以)

(a/b)mod p=a*c mod p = (a mod p * c mod p)mod p(定义 c为b在mod p意义下的逆元)

要是p不是素数 就比较恶心了 反正求逆元就是各种错...
质因数分解 然后把分母都给抹了去

这个地方也可以用卢卡斯定理来解决取模的问题

http://baike.sogou.com/v61779857.htm?fromTitle=Lucas%09%E6%95%B0%E8%AE%BA%E5%AE%9A%E7%90%86

模板位置:http://www.cnblogs.com/vongang/archive/2012/12/02/2798138.html(暂时不打算用,什么时候乘法逆元被卡了再说吧)

Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 

int Mi(int a,int b,int p)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%p;
        b>>=1;a=a*a%p;
    }return res;
}

int C(int n,int m,int p)
{
    if(m>n)return 0;
    return  f[n]*Mi(f[m]*f[n-m],p-2,p)%p;
}

int Lus(int n,int m,int p)
{
    if(m==0) return 1;
    return (C(n%p,m%p,p)*Lus(n/p,m/p,p))%p;
}

one、路径条数:从一个点(a,b)到另一个点(x,y)路径的总条数C(x-a+y-b,x-a)

two、给一个集合,一共n个元素,从中选取m个元素,满足选出的元素中没有相邻的元素,一共有c(n-m+1,m)种选法

three、求x1+x2+……+xn=m解的个数。利用插板法可以得出x1+x2+……+xn=m解的个数为C(n+m-1,m);

four、在一个长为n,宽为m的矩形当中,从(1,1)点到(n,m)的走法共有才C(n,n+m)种

 

插板法:http://baike.sogou.com/v10001101.htm?fromTitle=%E6%8F%92%E6%9D%BF%E6%B3%95

代码很简单就不写了吧、、、、、

常见题型:

1.将n个苹果放入m个盒子中,每个盒子至少放一个,问有多少种放法   sum=C(n-1,m-1)

2.将n个苹果放入m个盒子中,问有多少种放法     sum=C(n+m-1,m-1) 因为没有说箱子种至少放几个,那么箱子中的苹果数目可能         为0,这样我们就先在箱子中放入一个苹果,这样我们就相当于有n+m个苹果,要放入m个箱子中了

3.将n个苹果放入m个盒子中,第一个箱子可以放0个苹果,第二个盒子至少放一个苹果,第三个盒子最少放2个苹果、、、、问有多少       种放法         sum=C(n+1-1-2-3-、、、、-(m-2),m)     我们可以将第一个箱子看成已经放了一苹果,所以这个时候我们的苹果总     数要+1,然后我们第三到第m个箱子种放的苹果个数多于1个,我们可以认为我们从我们的n个苹果种先拿出i个放入第i个箱子中,这       样我们就可以认为我们的苹果总数为n+1-1-2-3-、、、、-(m-2)个

          八:快速幂

http://www.cnblogs.com/z360/p/6920954.html

int kpow(long long  n,long long  k,long long  mod)
{
     long long int res=1;
    while(k)
    {
        if(k&1) res=(res*n)%mod;
        n=(n*n)%mod;
        k=k>>1;//每次都将k/2,为使用的二进制,使改代码更快。 
    }
    return res;
}

快速乘:使乘法减速,但是防止在快速幂的时候报long long(在数小的时侯就尽量不要用了)

 

ll multi(ll a,ll b,ll m)
{
    ll ans=0;
    while(b)
    {
        if(b&1) (ans+=a) %= m;
        (a=a*2) %= m;
        b/=2;
    }
    return ans;
}
ll pow_mod(ll a,ll b,ll m)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=multi(res,a,m);  //这里要用到快速乘
        a=multi(a,a,m);
        b/=2;
    }
    return res;
}

 

 

 

 

       九:康拓展开

http://blog.csdn.net/acdreamers/article/details/7982067

 

康托展开表示的是当前排列在n个不同元素的全排列中的名次。比如213在这3个数所有排列中排第3。

 

那么,对于n个数的排列,康托展开为:

 

其中表示第i个元素在未出现的元素中排列第几。

 

举个简单易懂的例子:

 

对于排列4213来说,4在4213中排第3,注意从0开始,2在213中排第1,1在13中排第0,3在3中排第0,即:

 

,这样得到4213在所有排列中排第ans=20

 

康拓展开代码:

//康托展开  
LL Work(char str[])  
{  
    int len = strlen(str);  
    LL ans = 0;  
    for(int i=0; i<len; i++)  
    {  
        int tmp = 0;  
        for(int j=i+1; j<len; j++)  
            if(str[j] < str[i]) tmp++;  
        ans += tmp * f[len-i-1];  //f[]为阶乘  
    }  
    return ans;  //返回该字符串是全排列中第几大,从1开始  
}  
康拓展开

康托展开的逆运算:

void Work(LL n,LL m)  
{  
    n--;  
    vector<int> v;  
    vector<int> a;  
    for(int i=1;i<=m;i++)  
        v.push_back(i);  
    for(int i=m;i>=1;i--)  
    {  
        LL r = n % f[i-1];  
        LL t = n / f[i-1];  
        n = r;  
        sort(v.begin(),v.end());  
        a.push_back(v[t]);  
        v.erase(v.begin()+t);  
    }  
    vector<int>::iterator it;  
    for(it = a.begin();it != a.end();it++)  
        cout<<*it;  
    cout<<endl;  
}  
康拓展开的逆运算

求一个排列在全排列中的名次以及已知一个排列的名次,求该全排列

#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 21000
#define ll long long 
using namespace std;
char ch;
bool vis[N];
ll x,s[N],ans,ans1[N],n,m,t,sum,v[N],str[N];
ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
ll work1(ll x)//康拓展开的逆运算 
{
    memset(ans1,0,sizeof(ans1));
    for(int i=1;i<=n;i++)
     v[i]=i;
    for(int i=n;i>=1;i--)
    {
        t=x/s[i-1];
        x%=s[i-1];
        t++;
        sort(v+1,v+1+n);
        ans1[n-i+1]=v[t];
        v[t]=N;
    }
    for(int i=1;i<=n;i++)
     printf("%lld ",ans1[i]);
}
int work2()//康拓展开 
{
    for(int i=1;i<=n;i++)
    {
        sum=0;
        for(int j=i+1;j<=n;j++)
         if(str[j]<str[i]) sum++;//sum统计比当前数小的个数 
        ans+=s[n-i]*sum;//康拓展开公式,n-i=(n-i+1)-1; 
    }
    return ans++;//为什么++,因为我们求出的是有都少排列比这个排列小,那么到当前排列就要加1 
}
int main()
{
    n=read(),m=read();s[0]=1;
    for(int i=1;i<=n;i++)
     s[i]=s[i-1]*i;//预处理阶乘 
    while(m--)
    {
        cin>>ch;
        if(ch=='P')
        {
            x=read();
            x--;//为什么要减一??因为我们康拓展开是求的有多少数比当前的小,也就是说我们求出的是比当前排列要大一的排列,所以这个地方我们要减一 
            work1(x);
            printf("\n");
        }
        else
        {
            ans=0;
            for(int i=1;i<=n;i++) str[i]=read();
            work2();
            printf("%lld\n",ans);
        }
    }
}

 

          十:错排

https://baike.baidu.com/item/%E9%94%99%E6%8E%92%E5%85%AC%E5%BC%8F

错排:考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。

错排递推式:D(n)=(D(n-1)+D(n-2))*(n-1)

错排通项公式:D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!]

递推写法
    f[2]=1;
    for(int i=3;i<=n;i++) f[i]=(f[i-1]+f[i-2])*(i-1);     

高精

int cp()
{
    f[1][1]=0;f[2][1]=b[2]=b[1]=1;
    for(int i=3;i<=n;i++)
    {
        b[i]=max(b[i-1],b[i-2]);
        for(int j=1;j<=b[i];j++)
        {
            f[i][j]+=f[i-1][j]+f[i-2][j];
            f[i][j+1]+=f[i][j]/10;
            f[i][j]%=10;
        }
        while(f[i][b[i]+1])
        {
            b[i]++;
            f[i][b[i]+1]+=f[i][b[i]]/10;
            f[i][b[i]]%=10;
        } 
        for(int tmp=0,j=1;j<=b[i];j++)
        {
            f[i][j]=f[i][j]*(i-1)+tmp;
            if(f[i][j]>9)
            {
                tmp=f[i][j]/10;
                f[i][j]%=10;
                b[i]=max(b[i],j+1);
            }
            else tmp=0;
        }
        while(f[i][b[i]+1])
        {
            b[i]++;
            f[i][b[i]+1]+=f[i][b[i]]/10;
            f[i][b[i]]%=10;
        }
    }
}

 高精压位    http://www.cnblogs.com/hoskey/p/3722416.html

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 15100
#define mod 100000000 
using namespace std;
int n,s;
long long f[5001][N],l[5001];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int cp()
{
    f[1][1]=0,f[2][1]=l[1]=l[2]=1;
    for(int i=1;i<=n;i++)
    {
        l[i]=l[i-1];
        for(int j=1;j<=l[i];j++)
         f[i][j]=f[i-1][j]+f[i-2][j];
        for(int j=1;j<=l[i];j++)
        {
            f[i][j+1]+=f[i][j]/mod;
            f[i][j]%=mod;
        }
        while(f[i][l[i]+1]) l[i]++;
        for(int j=1;j<=l[i];j++)
         f[i][j]*=(i-1);
        for(int j=1;j<=l[i];j++)
        {
            f[i][j+1]+=f[i][j]/mod;
            f[i][j]%=mod;
        }
        while(f[i][l[i]+1]) l[i]++;
    }
}
int main()
{
    n=read();  
    cp();
    printf("%lld",f[n][l[n]]);
    for(int i=l[n]-1;i>=1;i--) printf("%08lld",f[n][i]);
    return 0;
}
高精压位后的错排

 

        十一:逆序对

http://www.cnblogs.com/z360/p/6921604.html

我们可以用逆序对的个数求将一列数排成有序的一列数交换的次数

 

void gsort(int l,int r)
{
    if(l==r) return ; int mid=(l+r)/2;
    gsort(l,mid);gsort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) tmp[k++]=a[i++];
        else
        {
            ans+=(long long)(mid-i+1);
            tmp[k++]=a[j++];    
        }
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(int i=l;i<=r;i++) a[i]=tmp[i];
 }

 

        十二:容斥原理

http://www.cnblogs.com/z360/p/6361230.html

 

 

       十三:数学公式

立方差公式:

(a+b)(a²-ab+b²)=a³+b³
(a-b)(a²+ab+b²)=a³-b³
(a-b)^3=(a-b)(a-b)(a-b)=(a^2-2ab+b^2)(a-b)=a^3-3a^2b+3ab^2-b^3
(a+b)^3=a^3+3a^2b-3ab^2+b^3

等差数列公式:

求第n项:①an=a1+(n-1)*d,d为公差                                       ②an=am+(n-m)*d

求前n项和:①S=n*(s1+sn)/2                                                     ②S=a1*n+n*(n-1)/2*d

等比数列公式:① S=a1*(1-q^n)/(1-q)         (q!=1)               ②S=a1*n      (q=1)

 

 

优先队列:

priority_queue<int>que; 大根堆,堆顶是最大的

priority_queue<int ,vector<int>, greator<int> >que 小根堆

 

          十四:杨辉三角

杨辉三角的性质

  1. 每个数等于它上方两数之和。
  2. 每行数字左右对称,由1开始逐渐变大。
  3. 第n行的数字有n项。
  4. 第n行数字和为2n-1。
  5. 第n行的m个数可表示为 C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数。
  6. 第n行的第m个数和第n-m+1个数相等 ,为组合数性质之一。
  7. 每个数字等于上一行的左右两个数字之和。可用此性质写出整个杨辉三角。即第n+1行的第i个数等于第n行的第i-1个数和第i个数之和,这也是组合数的性质之一。即 C(n+1,i)=C(n,i)+C(n,i-1)
  8. (a+b)n的展开式中的各项系数依次对应杨辉三角的第(n+1)行中的每一项。
  9. 将第2n+1行第1个数,跟第2n+2行第3个数、第2n+3行第5个数……连成一线,这些数的和是第4n+1个斐波那契数;将第2n行第2个数(n>1),跟第2n-1行第4个数、第2n-2行第6个数……这些数之和是第4n-2个斐波那契数。
  10. 将各行数字相排列,可得11的n-1(n为行数)次方:1=11^0; 11=11^1; 121=11^2……当n>5时会不符合这一条性质,此时应把第n行的最右面的数字"1"放在个位,然后把左面的一个数字的个位对齐到十位... ...,以此类推,把空位用“0”补齐,然后把所有的数加起来,得到的数正好是11的n-1次方。以n=11为例,第十一行的数为:1,10,45,120,210,252,210,120,45,10,1,结果为 25937424601=1110。
void Prepare()
{
    for(int i=1; i<=n; ++i)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1; j<i; ++j)
            C[i][j]=(C[i-1][j-1]%p+C[i-1][j]%p)%p;
    }
}

           十五、斯特林数(Stirling数

https://baike.baidu.com/item/%E6%96%AF%E7%89%B9%E6%9E%97%E6%95%B0/4938529?fr=aladdin

striling数分为两类,分别称为第一类stirling数跟第二类stirling数

第一类striling数

表示将 n 个不同元素构成m个圆排列的数目

 

无符号Stirling数有如下性质:

 

 

 

 

 

 

 

 

 

 

 

 

 证明可令升阶函数中的x=1,比较两边系数。
有符号stirling性质类似:

 

 

  ,注意   。证明可令降阶函数中的x=1,比较两边系数。

 

 

 

 

第二类striling数

表示将n个不同的元素拆分成m个集合的方案数,和第一类Stirling数不同的是,集合内是不考虑次序的,而圆排列是有序的。常常用于解决组合数学中几类放球模型。描述为:将n个不同的球放入m个无差别的盒子中,要求盒子非空,有几种方案?

 

转载于:https://www.cnblogs.com/z360/p/7337175.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值