数论

重要知识点

1.最大公约数

ll gcd(ll x,ll y){
    if (!x || !y) return x+y;
    if (x%y==0) return y;
    return gcd(y,x%y);
}

2.扩展欧几里得

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

3.逆元(mod为素数)

//O(n)求1~n的逆元
inv[1]=1;  
    for (int i=2;i<=n;++i)  
        inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;  

//求x的逆元
ll pow_mod(ll x, ll n)
{  
    ll res=1;  
    while(n>0)
    {  
        if(n&1)res=res*x%mod;  
        x=x*x%mod;  
        n>>=1;  
    }  
    return res;  
}  
pow_mod(x,mod-2);

4.素数线性筛

v[1]=1;
    for (int i=2;i<=N;i++)
    {
        if (!v[i])
            p[++tot]=i;   
        for (int j=1;p[j]*i<=N;j++)
        {
            v[p[j]*i]=1;
            if (!(i%p[j])) break;     
        }   
    }   

5.欧拉函数

int m[maxn],phi[maxn],p[maxn],pt;
//m[i]是i的最小素因数,p是素数,pt是素数个数

void make()
{
    phi[1]=1;
    int N=maxn;
    int k;
    for(int i=2;i<N;i++)
    {
        if(!m[i])//i是素数
            p[pt++]=m[i]=i,phi[i]=i-1;
        for(int j=0;j<pt&&(k=p[j]*i)<N;j++)
        {
            m[k]=p[j];
            if(m[i]==p[j])//为了保证以后的数不被再筛,要break
            {
                phi[k]=phi[i]*p[j];
/*这里的phi[k]与phi[i]后面的∏(p[i]-1)/p[i]都一样(m[i]==p[j])只差一个p[j],就可以保证∏(p[i]-1)/p[i]前面也一样了*/
                break;    
            }
            else
                phi[k]=phi[i]*(p[j]-1);
                //积性函数性质,f(i*k)=f(i)*f(k)
        }
    }

    //本人写法
    for (int i=2;i<N;i++){
        if (!v[i]) p[++tot]=i,phi[i]=i-1;
        for (int j=1;j<=tot && i*p[j]<N;j++){
            v[i*p[j]]=1;
            if (i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            } else phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
}//其实就是在素数线性筛上动动手脚就好了~

6.排列组合(预处理)

void init(){
    c[0][0]=c[1][0]=c[1][1]=1;  
    for (int i=2;i<=1000;i++){   
        c[i][0]=1;
        for (int j=1;j<=i;j++)   
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
}

7.莫比乌斯反演

8.高斯消元

9.线性基

10.第一类斯特林数

s(p,k)的一个的组合学解释是:将p个物体排成k个非空循环排列的方法数。

s(p,k)的递推公式:
s(p,k)=(p1)s(p1,k)+s(p1,k1),1kp1
边界条件:
S(p,p)=1,p0
S(p,0)=0,p1

递推关系的说明:
考虑第p个物品,p可以单独构成一个非空循环排列,这样前p-1种物品构成k-1个非空循环排列,方法数为s(p-1,k-1);
也可以前p-1种物品构成k个非空循环排列,而第p个物品插入第i个物品的左边,这有(p-1)*s(p-1,k)种方法。

    for (int i=0;i<N;i++){
        s[i][0]=0;s[i][i]=1;
        for (int j=1;j<i;j++)
            s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j]%mod)%mod;
    }

11.第二类斯特林数

S(p,k)的一个组合学解释是:将p个物体划分成k个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数。
k!S(p,k)是把p个人分进k间有差别(如:被标有房号)的房间(无空房)的方法数。
  
S(p,k)的递推公式是:
S(p,k)=kS(p1,k)+S(p1,k1),1kp1
边界条件:
S(p,p)=1,p0
S(p,0)=0,p1
  
递推关系的说明:
考虑第p个物品,p可以单独构成一个非空集合,此时前p-1个物品构成k-1个非空的不可辨别的集合,方法数为S(p-1,k-1);
也可以前p-1种物品构成k个非空的不可辨别的集合,第p个物品放入任意一个中,这样有k*S(p-1,k)种方法。

    for(int i=1;i<N;i++){  
        s[i][i]=1;s[i][0]=0;  
        for(j=1;j<i;j++)   
            s[i][j]=(s[i-1][j-1]+j*s[i-1][j]%mod)%mod;  
    }  

12.fft

struct cp
{
    double x,y;
    inline cp(double _x=0.0,double _y=0.0)
    {
        x=_x;
        y=_y;
    }
    friend inline cp operator + (cp a,cp b)
    {
        return cp(a.x+b.x,a.y+b.y);
    }
    friend inline cp operator - (cp a,cp b)
    {
        return cp(a.x-b.x,a.y-b.y);
    }
    friend inline cp operator * (cp a,cp b)
    {
        return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
    }
}a[N],b[N],c[N],t[N];

void fft(cp *a,int len,int kd)
{
    //kd=1时将系数表达式转换为点值表达式(就是n个点描述一个多项式)
    //kd=-1时将点值表达式转换成系数表达式
    for (int i=0;i<len;i++) t[i]=a[rev[i]];
    for (int i=0;i<len;i++) a[i]=t[i];
    //低级写法,ntt的写法更优高级

    for (int i=2;i<=len;i*=2)
    {
        cp wn(cos(2*pi/i),kd*sin(2*pi/i));//公比
        for (int k=0;k<len;k+=i)
        {
            cp w(1,0);//螺旋因子
            for (int j=0;j<i/2;j++)
            {
                cp x=a[k+j];//蝶形操作
                cp y=a[k+j+i/2]*w;
                a[k+j]=x+y;//前半部分
                a[k+j+i/2]=x-y;//后半部分
                w=w*wn;
            }
        }
    }
    if(kd==-1)///IDFT  
    for(int i=0;i<len;i++)  
        a[i].x=round(a[i].x/len);//4舍5入  
}

//rev[]的预处理
    for(m=n,n=1;n<=m<<1;n<<=1) l++;
    for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<l);

13.ntt

void ntt(ll *a,int len,int f) {
    for (int i=0;i<len;i++)
        if (rev[i]>i) swap(a[i],a[rev[i]]);
    for(int i=2;i<=len;i<<=1) {
        ll wn=power(3,((mod-1)/i*f+mod-1)%(mod-1)),m=i>>1;
        //3为mod的原根
        for(int j=0;j<len;j+=i) {
            ll w=1;
            for(int k=0;k<m;k++,w=w*wn%mod) {
                ll x=a[j+k],y=a[j+k+m]*w%mod;
                a[j+k]=(x+y)%mod,a[j+k+m]=(x-y+mod)%mod;
            }
        }
    }
    if(f==-1) {
        ll ni=power(len,mod-2);
        for(int i=0;i<len;i++) a[i]=a[i]*ni%mod;
    }
}

14.多项式求逆

void getinv(ll *a,ll *b,int len){
    static ll t[N];
    if (len==1){
        b[0]=power(a[0],mod-2);
        return;
    }
    getinv(a,b,len/2);
    memcpy(t,a,sizeof(a[0])*len);
    memset(t+len,0,sizeof(a[0])*len);
    int m=len,l=-1,tn=len;
    for (len=1;len<=m;len*=2)l++;
    for (int i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
    ntt(t,len,1),ntt(b,len,1);
    for (int i=0;i<len;i++)
        t[i]=b[i]*(2ll-t[i]*b[i]%mod+mod)%mod;
    ntt(t,len,-1);
    for (int i=0;i<(len<<1);i++)
        b[i]=t[i];
    memset(b+tn,0,sizeof(b[0])*tn);
    len=tn;
}


    getinv(b,invb,l);

15.多项式开根

void getsqrt(ll *a,ll *b,int len){
    static ll t[N],invb[N];
    if (len==1){
        b[0]=1;
        return;
    }
    getsqrt(a,b,len/2);
    memset(invb,0,sizeof(a[0])*len);
    getinv(b,invb,len);
    memcpy(t,a,sizeof(a[0])*len);
    memset(t+len,0,sizeof(a[0])*len);
    int m=len,l=-1,tn=len;
    for (len=1;len<=m;len*=2)l++;
    for (int i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<l);
    ntt(t,len,1);ntt(b,len,1);ntt(invb,len,1);
    for (int i=0;i<len;i++)
        t[i]=inv2*(b[i]+invb[i]*t[i]%mod)%mod;
    ntt(t,len,-1);
    for (int i=0;i<(len>>1);i++)
        b[i]=t[i];
    memset(b+tn,0,sizeof(b[0])+tn);
    len=tn;
}

多项式需注意的地方:
1.多项式求逆和开根是函数里的变量不要和外面的全局变量重名。
2.memset和memcpy是不要弄错了位数。
3.注意每次搞完之后要去掉哪些位(保留题目中要求的就好了)或是将后面的为往前加(求和余数)

16.中国剩余定理(CRT)

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;  
}  

17.卡特兰数

定义

hn 为多边形划分为一个个三角形的方案数(区域内不相交)。 h1=1
hn=n1k=1hkhnk=1n(2n2n1)=4n2n1hn1

应用

18.类欧几里得

calc(a,b,c,n)=x=0nax+bc

ll calc(ll a,ll b,ll c,ll n){
    ll s=0,t;
    if (a>=c) t=a/c,a-=c*t,s+=n*(n+1)/2*t;
    if (b>=c) t=b/c,b-=c*t,s+=(n+1)*t;
    if (a==0) return s+(n+1)*(b/c);
    ll m=(a*n+b)/c;
    s+=n*m;
    return s+calc(c,c-b-1,a,m-1); 
}

一堆题

一些定理

MillerRabin

费马小定理:

对于素数p和任意整数a,有 apa(modp) (同余)。反过来,满足 apa(modp) ,p也几乎一定是素数。

伪素数:

如果n是一个正整数,如果存在和n互素的正整数a满足 an11(modn) ,我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数。

Miller-Rabin测试:

不断选取不超过n-1的基b(s次),计算是否每次都有 bn11(modn) ,若每次都成立则n是素数,否则为合数。 

二次探测定理:

如果p是奇素数,则 x21(modp) 的解为 x = 1 || x = p - 1(mod p);

具体实现过程:

an11(modn) 那么它几乎肯定是素数。
r=n1 ,则 r=2kp
此时若为质数必须满足:
(1) ar1(modn)
m=ar
(2)若 m211(modn) m20=1||m20=n1
(3)若 m221(modn) m21=1||m21=n1
…….
…….
…….
…….若 m2k1(modn) m2k1=1||m2k1=n1

Pollardrho

//hdu3864 AC code
#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <ctime>  
#include <iostream>  
#include <algorithm>  
using namespace std;  
typedef __int64 LL;  
const LL NUM=10;//运算次数,Miller_Rabin算法为概率运算,误判率为2^(-NUM);  
LL t,f[100];  
LL mul_mod(LL a,LL b,LL n)//求a*b%n,由于a和b太大,需要用进位乘法  
{  
    a=a%n;  
    b=b%n;  
    LL s=0;  
    while(b)  
    {  
        if(b&1)  
            s=(s+a)%n;  
        a=(a<<1)%n;  
        b=b>>1;  
    }  
    return s;  
}  
LL pow_mod(LL a,LL b,LL n)//求a^b%n  
{  
    a=a%n;  
    LL s=1;  
    while(b)  
    {  
        if(b&1)  
            s=mul_mod(s,a,n);  
        a=mul_mod(a,a,n);  
        b=b>>1;  
    }  
    return s;  
}  
bool check(LL a,LL n,LL r,LL s)  
{  
    LL ans,p,i;  
    ans=pow_mod(a,r,n);  
    p=ans;  
    for(i=1;i<=s;i++)  
    {  
        ans=mul_mod(ans,ans,n);  
        if(ans==1&&p!=1&&p!=n-1)return true;  
        p=ans;  
    }  
    if(ans!=1)return true;  
    return false;  
}  
bool Miller_Rabin(LL n)//Miller_Rabin算法,判断n是否为素数  
{  
    if(n<2)return false;  
    if(n==2)return true;  
    if(!(n&1))return false;  
    LL i,r,s,a;  
    r=n-1;s=0;  
    while(!(r&1)){r=r>>1;s++;}  
    for(i=0;i<NUM;i++)  
    {  
        a=rand()%(n-1)+1;  
        if(check(a,n,r,s))  
            return false;  
    }  
    return true;  
}  
LL gcd(LL a,LL b)  
{  
    return b==0?a:gcd(b,a%b);  
}  
LL Pollard_rho(LL n,LL c)//Pollard_rho算法,找出n的因子  
{  
    LL i=1,j,k=2,x,y,d,p;  
    x=rand()%n;  
    y=x;  
    while(true)  
    {  
        i++;  
        x=(mul_mod(x,x,n)+c)%n;  
        if(y==x)return n;  
        if(y>x)p=y-x;  
        else p=x-y;  
        d=gcd(p,n);  
        if(d!=1&&d!=n)return d;  
        if(i==k)  
        {  
            y=x;  
            k+=k;  
        }  
    }  
}  
void find(LL n)//找出n的所有因子  
{  
    if(Miller_Rabin(n))  
    {  
        f[t++]=n;//保存所有因子  
        return;  
    }  
    LL p=n;  
    while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);//由于p必定为合数,所以通过多次求解必定能求得答案  
    find(p);  
    find(n/p);  
}  
int main()  
{  
    srand(time(NULL));//随机数设定种子  
    LL n;  
    while(cin>>n)  
    {  
        if(n==1){cout<<"is not a D_num"<<endl;continue;}//特判  
        t=0;  
        find(n);  
        if(t!=2&&t!=3){cout<<"is not a D_num"<<endl;continue;}  
        sort(f,f+t);  
        if(t==2)  
        {  
            if(f[0]!=f[1])cout<<f[0]<<" "<<f[1]<<" "<<n<<endl;  
            else cout<<"is not a D_num"<<endl;  
        }  
        else//n是一个素数的三次方  
        {  
            if(f[0]==f[1]&&f[1]==f[2])cout<<f[0]<<" "<<f[0]*f[0]<<" "<<n<<endl;  
            else cout<<"is not a D_num"<<endl;  
        }  
    }  
    return 0;  
}

Lucas定理

这里写图片描述
这里写图片描述
这里写图片描述
证明:orz

Bell数

Bell数的定义:第n个Bell数表示集合{1,2,3,…,n}的划分方案数,即:B[0] = 1;

每一个Bell数都是第二类Stirling数的和,即:
这里写图片描述
Bell数的指数生成函数是:
这里写图片描述
Bell数的同余性质
这里写图片描述这里写图片描述
Bell数模素数p的周期为:
这里写图片描述

void Bell(int T[],int MOD)  
{  
    B[0] = 1;  
    B[1] = 1;  
    T[0] = 1;  
    for(int i=2;i<N;i++)  
    {  
        T[i-1] = B[i-1];  
        for(int j=i-2;j>=0;j--)  
            T[j] = (T[j]+T[j+1])%MOD;  
        B[i] = T[0];  
    }  
}  

中国剩余定理(CRT)

设正整数两两互素,则同余方程组
这里写图片描述
有整数解。并且在模 M=m1m2...mk 下的解是唯一的,解为
这里写图片描述

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N=100;
ll n,a[N],m[N],ans;

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

ll gao(){
    ll tot=1;
    for (int i=1;i<=n;i++)
        tot*=m[i];
    for (int i=1;i<=n;i++){
        ll mii,y,mi=tot/m[i];
        extend(mi,m[i],mii,y);
        while (mii<0) mii+=m[i];
        ans=(ans+a[i]*mi%tot*mii%tot)%tot;
    }
    return ans;
}

int main(){
    scanf("%lld",&n);
    for (int i=1;i<=n;i++)
        scanf("%lld%lld",&a[i],&m[i]);
    printf("%lld\n",gao());
}

若正整数两两不互素,则%%%orz吧~
orz

多项式求逆

现在的问题是求A的逆 modxn A1(x)
A(x)A1(x)1(modxn)
显然但 n=1 A(x)c(modxn)
A1(x)c1(modxn)

B(x)=A1(x)
A(x)B(x)1(modxn)
A(x)B(x)1(modxn2)
A(x)B(x)1(modxn2)
B(x)B(x)0(modxn2)
B2(x)2B(x)B(x)+B2(x)0(modxn)
B(x)2B(x)+A(x)B2(x)0(modxn)
B(x)2B(x)A(x)B2(x)(modxn)
也就是说我们可以分治一波,边界就是 n=1

Sierpinski三角形

这里写图片描述

这里写图片描述

#include<cstdio>
int main()
{
    const int n=(1<<5)-1;
    int i,j;
    for (i=0; i<=n; i++)
    {
        for (j=0; j<=n; j++)
           printf( (i&j)==j ? "#" : " ");/*☆☆☆☆☆*/
        printf("\n");
    }    
    getchar();
    return 0;
}
输出:
#                               
##                              
# #                             
####                            
#   #                           
##  ##                          
# # # #                         
########                        
#       #                       
##      ##                      
# #     # #                     
####    ####                    
#   #   #   #                   
##  ##  ##  ##                  
# # # # # # # #                 
################                
#               #               
##              ##              
# #             # #             
####            ####            
#   #           #   #           
##  ##          ##  ##          
# # # #         # # # #         
########        ########        
#       #       #       #       
##      ##      ##      ##      
# #     # #     # #     # #     
####    ####    ####    ####    
#   #   #   #   #   #   #   #   
##  ##  ##  ##  ##  ##  ##  ##  
# # # # # # # # # # # # # # # # 
################################

奇怪思想

把水倒掉

将一个陌生的问题转换成熟悉的问题。
将一陀问题转化成两陀小问题再变成四陀小小问题….

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值