• 关于OI中的各种数学


    学到后面数学越来越多了,感觉好难啊,开个博客专门记录一下数学相关的东西

    因为反正也没人看,所以主要还是给自己看的

    一些符号:

    数论函数的卷积:$ast$,$ h = f ast g$$h(n)=sum_{d|n}f(d)g(frac{n}{d})$

    $epsilon $ 叫单位元,对每个 $f(1)  eq 0$ 的函数 $f$,有 $epsilon ast f = f$

    $epsilon(n) = [ n=1 ]$ 

    $mathbf{id}$ ,不知道叫啥,反正 $mathbf{id}(n)=n$ ,没了

    $mathbf{1}$ ,奇怪的操作,数字 $1$ 也能当函数,$mathbf{1}(n)=1$

    $phi $,欧拉函数,有很多结论:

    $mathbf{id}=phi ast mathbf{1}$, $n=sum_{d|n}phi(d)$

    对于某个函数 $f$ 的逆 $g$,有 $f ast g = epsilon$

    $mu $,莫比乌斯函数,$mu $ 其实是 $mathbf{1}$ 的逆

    所以有 $phi = mu ast mathbf{id}$

    关于如何求出 $mu(n)$ 的值 :

    如果 $n$ 质因数分解以后每个质因数都互不相同,设质因数数量为 $p$,则 $mu(n)=(-1)^p$否则 $mu(n)=0$

    $sum_{d|n}mu(d)=[n=1]$

    莫比乌斯反演: 设 $F(n)=sum_{d|n}f(d)$,那么有 $f(n)=sum_{d|n}mu(frac {n} {d})F(d)$

    证明:

    $f(n)=sum_{m|n}[frac {n} {m}=1] f(m)$

    $f(n)=sum_{m|n}sum_{d|frac {n} {m}}mu(d)f(m)$,枚举 $d$

    $f(n)=sum_{d|n}mu(d)sum_{m|frac {n} {d}}f(m)$

    $f(n)=sum_{d|n}mu(d)F(frac {n} {d})$

    $f(n)=sum_{d|n}mu(frac {n} {d})F(d)$

     

    另一个方向的结论:$F(n)=sum_{n|d}f(d)$,那么有 $f(n)=sum_{n|d}mu(frac {d} {n})F(d)$

    证明和上面差不多

    二项式反演(就是容斥):

    首先

    $sum_{k=0}^{n}(-1)^kinom{n}{k}=[n=0]$,证明可以通过递推式,数学归纳法解决

    $n=0$ 时式子为 $1$,$n$ 等于 $1$ 时式子为 $0$,考虑 $n+1$ 都是由 $n$ 得到的

    对于每一个 $inom{n}{k}$ 他会贡献给 $inom{n+1}{k},inom{n+1}{k+1}$,因为 $(-1)^k,(-1)^{k+1}$ 正负不同刚好抵消,所以 $n+1$ 还是 $0$

    $F(n)=sum_{k=0}^{n}inom{n}{k}f(k)$,那么有 $f(n)=sum_{k=0}^{n}(-1)^{n-k}inom{n}{k}F(k)$

    证明:

    $f(n)=sum_{m=0}^{n}[n-m=0]inom{n}{m}f(m)$

    $f(n)=sum_{m=0}^{n}sum_{k=0}^{n-m}(-1)^kinom{n-m}{k}inom{n}{m}f(m)$

    发现 $inom{n-m}{k}inom{n}{m}$ 意思是 $n$$m$ 剩下的再选 $k$ ,和 $n$$k$,剩下选 $m$ 是一样的

    $f(n)=sum_{m=0}^{n}sum_{k=0}^{n-m}(-1)^kinom{n}{k}inom{n-k}{m}f(m)$,枚举 $k$

    $f(n)=sum_{k=0}^{n}(-1)^kinom{n}{k}sum_{m=0}^{n-k}inom{n-k}{m}f(m)$

    $f(n)=sum_{k=0}^{n}(-1)^kinom{n}{k}F(n-k)$,换一下下标:

    $f(n)=sum_{k=0}^{n}(-1)^{n-k}inom{n}{k}F(k)$

    杜教筛相关:

    $S_{f}$ 表示 $f$ 的前缀和,即 $S_{f}(n)=sum_{i=1}^{n}f(i)$

    $S_{f ast g} = sum_{d=1}^{n} g(d)S_{f}(left lfloor frac{n}{d} ight floor)$

    证明:

    $sum_{i=1}^{n}sum_{d|i}f(d)g(frac {i} {d})=sum_{i=1}^{n}sum_{d|i}f(frac {i} {d})g(d)$

    考虑枚举因数 $d$,显然有 $n/d$$i$,并且每种都是 $d$ 的倍数,设 $i=dj$

    $=sum_{d=1}^{n}g(d)sum_{j}^{left lfloor frac{n}{d} ight floor}f(j)$

    $= sum_{d=1}^{n} g(d)S_{f}(left lfloor frac{n}{d} ight floor)$

    所以有 $g(1)S_f(n)=S_{f ast g}-sum_{i=2}^{n} g(i)S_{f}(left lfloor frac{n}{i} ight floor)$

    因为 $mu ast mathbf{1} = epsilon$, $S_{epsilon}(n)=1$

    所以 $S_{mu}(n)=1-sum_{i=2}^{n}1 cdot S_{mu}(left lfloor frac{n}{i} ight floor)$

    $S_{mu}(n)=1-sum_{i=2}^{n}S_{mu}(left lfloor frac{n}{i} ight floor)$

    因为 $phi ast mathbf{1} = mathbf{id}$,$S_{mathbf{id}}(n)=frac {n(n+1)} {2}$

    所以 $S_{phi}(n)=frac {n(n+1)} {2} - sum_{i=2}^{n} 1 cdot S_{phi}(left lfloor frac{n}{i} ight floor)$

    $S_{phi}(n)=frac {n(n+1)} {2} - sum_{i=2}^{n}S_{phi}(left lfloor frac{n}{i} ight floor)$

    $f=phi cdot id$,因为 $(f ast mathbf{id})(n)=sum_{d|n}f(d) cdot (frac {n} {d})= sum_{d|n}phi(d) cdot d cdot (frac {n} {d})=nsum_{d|n}phi(d)=n^2$

    所以 $S_{f ast mathbf{id}}(n)=n^2$

    所以 $S_{f}(n)=n^2 - sum_{i=2}^{n}i cdot S_{f}(left lfloor frac{n}{i} ight floor)$

    生成函数相关:

    $sum_{n=0}^{infty }x^n = frac {1} {1-x}$

    $sum_{n=0}^{infty }inom{n+k-1}{n}x^n = frac {1} {(1-x)^k}$

    $sum_{n=0}^{infty }frac {x^n} {n!} = e^x$

    $sum_{n=0}^{infty }frac {x^n} {n} = ln frac {1} {1-x}$

    $sum_{n=0}^{infty }frac {x^{2n}} {(2n)!} = frac {e^x+e^{-x}} {2}$

    $sum_{n=0}^{infty }frac {x^{2n+1}} {(2n+1)!} = frac {e^x-e^{-x}} {2}$

    泰勒展开:$F(x)=sum_{i=0}^{infty} frac {F^{(i)}(x_0)(x-x_0)^i} {i!}$

    群论相关:

    符号:

    $G$ 置换群

    $Z_k$ 保持 $k$ 这个位置不变的置换集合

    $E_k$ $k$ 这个位置改变的的元素集合

    $C(pi)$ 置换 $pi$ 作用下不变的位置 $k$ 的个数

    $Burnside$ 引理: $L= frac{1} {left |G  ight |}sum_{i=1}^{n}left | Z_i ight |=frac{1} {left |G  ight |}sum_{pi in G}C(pi)$

    $Polya$ 定理: $ L= frac {1} {left |G  ight |} (m^{c(pi_1)}+m^{c(pi_2)}+m^{c(pi_3)}+...+m^{c(pi_p)})$

    其中 $m$ 是颜色数,$c(pi_k)$ 是置换 $pi_k$ 的循环节个数

    博弈论(以下均为口胡)

    博弈的每个状态都可以看成点,一个状态经过一些操作到达另一个状态看成有向边,整个博弈是个 $DAG$ 

    一些定义:$mex$ ,一种对整数集合的操作,输入一个集合,输出一个数,为集合中没出现的最小的整数,如 $mex(0,1,3,4)=2,mex(1,2,3)=0$

    $SG$ 函数,定义为所有它能到达的点的 $SG$ 值的 $mex$,如果它不能到达任何点,$SG=0$

    结束局面为必败局面,能到达必败局面的是必胜局面,所有到达的局面都是必胜局面的是必败局面(十分显然)

    发现这样和 $SG$ 有很大关联,如果 $SG>0$ 说明有一个后继局面 $SG=0$,如果 $SG=0$ 说明不是结束局面就是所有到达的局面 $SG>0$

    所以对于单个博弈如果 $SG=0$ 则为必败局面,$SG>0$ 则为必胜局面

    对于多个子博弈一起进行的博弈(子博弈之间互不干扰),这个总的博弈的 $SG$ 为所有子博弈 $SG$ 的异或和(严谨证明好像要很多神仙操作,看不懂溜了)

    总结一下就是单个博弈 $SG$ 是取 $mex$ ,多个博弈 $SG$ 取 $xor$

    $Anti-SG$,面对没有后继的状态的人赢,如果规定当局面所有单一游戏 $SG$ 都是 $0$ 时游戏结束

    那么先手必胜当且仅当:

    整个游戏的 $SG>0$ 并且存在子博弈的 $SG>1$,或者

    整个游戏的 $SG=0$ 并且任意子博弈的 $SG<=1$

    具体证明我也讲不清楚,大概就是把 整个游戏的 $SG$ 是否为 $0$,是否存在子博弈 $SG>1$ ,分四种情况情况分别讨论

    然后根据结束状态 总 $SG=0$,子 $SG=0$ 数学归纳一下,真想探究的话走这边:传送门

    $multi-SG$ ,单一博弈的一个后继可以是 多个单一博弈的总博弈 ,这个东西同样满足 $SG$ 函数

    即每个博弈的 $SG$ 值为所有后继博弈的 $SG$ 值异或和

     计算几何各种基础操作(毒瘤警告):

     自己整理的,并不能保证正确性 $qwq$,如果有大佬发现错误希望能说一声,感激不尽 $qwq$

    以下总计 $6.32kb$

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    typedef double db;
    typedef long double ldb;
    const db eps=1e-12;
    头文件
    inline int dcmp(db x) { if(fabs(x)<eps) return 0; return x<0 ? -1 : 1; }//判断正负
    判断正负
    struct Point {//定义点或者向量
        db x,y;
        Point (db a=0,db b=0) { x=a,y=b; }
        inline Point operator + (const Point &tmp) const {//向量加
            return Point(x+tmp.x,y+tmp.y);
        }
        inline Point operator - (const Point &tmp) const {//向量减
            return Point(x-tmp.x,y-tmp.y);
        }
        inline Point operator * (const db k) const {//向量数乘
            return Point(x*k,y*k);
        }
        inline bool operator != (const Point &tmp) const {
            return fabs(x-tmp.x)>eps||fabs(y-tmp.y)>eps;
        }
        inline bool operator < (const Point &tmp) const {//把点按x,y排序
            return x!=tmp.x ? x<tmp.x : y<tmp.y;
        }
        inline void print() { cout<<x<<" "<<y<<endl; }//输出
    };
    定义点或向量
    inline db Cross(Point A,Point B) { return A.x*B.y-A.y*B.x; }//叉积
    叉积
    inline db Dot(Point A,Point B) { return A.x*B.x+A.y*B.y; }//点积
    点积
    inline bool In_Line(Point A,Point B,Point C) {//判断A是否在BC上
        if(fabs(Cross(B-A,C-A))<eps&& Dot(B-A,C-A)<-eps) return 1;
    }
    判断A是否在BC上
    inline db Polar_Angle(Point A) {//求向量A与x轴的极角
        return atan2(A.y,A.x);
    }
    求向量A与x轴的极角
    inline Point Rotate(Point A,db a) {//把向量A旋转弧度a
        return Point(A.x*cos(a)-A.y*sin(a),A.x*sin(a)+A.y*cos(a));
    }
    把向量A旋转弧度a
    inline db Length(Point A) {//求向量A的长度
        return sqrt(Dot(A,A));
    }
    求向量A的长度
    inline db Angle(Point A,Point B) {//求向量A,B之间的夹角
        return acos(Dot(A,B)/Length(A)/Length(B));
    }
    求向量A,B之间的夹角
    inline db Distance_To_Line(Point P,Point A,Point B) {//求P到直线AB的距离
        return fabs(Cross(B-A,P-A)/Length(B-A));
    }
    求P到直线AB的距离
    inline Point Intersection(Point a1,Point a2,Point b1,Point b2)//求直线a1a2,b1b2的交点
    {
        Point a=a2-a1,b=b2-b1,c=b1-a1;
        if(fabs(Cross(b,a))<eps) return Point(-1e9,-1e9);
        db t=Cross(b,c)/Cross(b,a);
        return a1+a*t;
    }
    求直线a1a2,b1b2的交点
    void Tubao1()//按x,y排序求凸包
    {
        sort(P+1,P+n+1); st[++Top]=P[1];
        for(int i=2;i<=n;st[++Top]=P[i],i++)
            while(Top>1&& Cross(P[i]-st[Top-1],st[Top]-st[Top-1])>-eps ) Top--;
        //此处忽略加入凸包集合的代码
        st[Top=1]=p[n];
        for(int i=n-1;i;st[++Top]=P[i],i--)
            while(Top>1 && Cross(P[i]-st[Top-1],st[Top]-st[Top-1])>-eps ) Top--;
        //此处忽略加入凸包集合的代码
    }
    按x,y排序求凸包
    inline bool cmp(const Point &A,const Point &B) { return Cross(A,B)>0||( Cross(A,B)==0&&Length(A)<Length(B) ); }//按极角排序
    按极角排序
    void Tubao2()//按极角求凸包
    {
        sort(P+1,P+n+1); for(int i=1;i<=n;i++) A[i]=A[i]-A[1];
        sort(P+1,P+n+1,cmp);
        for(int i=1;i<=n;st[++Top]=P[i],i++)
            while(Top>1 && Cross(P[i]-st[Top-1],st[Top]-st[Top-1])>-eps ) Top--;
        n=Top; for(int i=1;i<=n;i++) P[i]=st[i];
    }
    按极角求凸包
    inline bool Is_Point_In_Polygon(Point P,Point *poly)//判断点是否在多边形内
    {
        /*从下往上穿过射线的边包含起点不包含终点
        从上往下穿过射线的边包含终点不包含起点
        这样若穿过的端点所在的两边同向则只被计算一次
        若穿过的端点所在的边反向则要么一次都不计算,要么直接算两次*/
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(In_Line(P,poly[i],poly[i%n+1])) return 1;
            int k=dcmp( Cross(poly[i%n+1]-poly[i],P-poly[i]) );
            int d1=dcmp(poly[i].y-P.y);
            int d2=dcmp(poly[i%n+1].y-P.y);
            if(k>0&&d1<=0&&d2>0) cnt++;
            if(k<0&&d2<=0&&d1>0) cnt++;
        }
        return cnt&1;
    }
    判断点是否在多边形内
    inline bool Is_Point_In_Tubao(Point P,Point *poly)//判断点P是否在凸包内,此处凸包按极角排序,poly[1]=(0,0)
    {
        if(Cross(P,poly[1])>eps||Cross(poly[n],P)>eps) return 0;
        ll pos=lower_bound(poly+1,poly+n+1,P,cmp)-poly-1;
        return dcmp( Cross(P-poly[pos],poly[pos%n+1]-poly[pos]) )<=0;
    }
    判断点P是否在凸包内
    inline db Get_Area(Point *poly)//求多边形面积,此处多边形按极角排序
    {
        db res=0;
        for(int i=2;i<n;i++) res+=Cross(poly[i]-poly[1],poly[i+1]-poly[1]);
        return res/2;
    }
    求多边形面积
    inline Point Get_Triangle_Center_of_gravity(Point A,Point B,Point C) {
        return Point((A.x+B.x+C.x)/3,(A.y+B.y+C.y)/3);
    }
    求三角形重心
    struct Line {//定义有向直线
        Point p,v; db ang;
        Line (Point A,Point B) { p=A,v=B; ang=atan2(v.y,v.x); }
        inline bool Is_Right(Point G) { return Cross(G-p,v)>0; }//判断G是否在直线右边
    };
    定义有向直线
    inline bool cmp2(Line &A,Line &B) { return dcmp(A.ang-B.ang)!=0 ? A.ang<B.ang : B.Is_Right(A.p); }//把直线按极角排序
    把直线按极角排序
    inline Point Intersection(Line A,Line B)//求直线交点
    {
        Point u=A.p-B.p,v=A.v,w=B.v;
        db t=Cross(w,u)/Cross(v,w);
        return A.p+A.v*t;
    }
    求直线交点
    void Get_Half_Plane_Intersection(Line *P)//半平面交
    {
        sort(P+1,P+n+1,cmp2); int m=n,L=1,R=0; n=0;
        for(int i=1;i<=m;i++) if( dcmp(P[i].ang-P[i+1].ang)!=0||i==m ) P[++n]=P[i];
        for(int i=1;i<=n;i++)
        {
            while(L<R && P[i].Is_Right(Intersection(Q[R],Q[R-1])) ) R--;
            while(L<R && P[i].Is_Right(Intersection(Q[L],Q[L+1])) ) L++;
            Q[++R]=P[i];
        }
        while(L<R && Q[L].Is_Right(Intersection(Q[R],Q[R-1])) ) R--;
        while(L<R && Q[R].Is_Right(Intersection(Q[L],Q[L+1])) ) L++;
        n=0; Q[R+1]=Q[L];
        for(int i=L;i<=R;i++) P[++n]=Intersection(Q[i],Q[i+1]);
    }
    半平面交
    inline db Get_Triangle_Area(Point A,Point B,Point C) {//求三角形面积
        return fabs(Cross(B-A,C-A)/2);
    }
    求三角形面积
    //旋转卡壳
    void RotatingCaliper_diameter(Point *poly)
    {
        db res=0;
        for(int i=1,p=1;i<=n;i++)
        {
            //可以同时卡多个点
            while( Get_Triangle_Area(poly[i],poly[i+1],poly[p%n+1]) >= Get_Triangle_Area(poly[i],poly[i+1],poly[p]) ) p=p%n+1;
            res=max(res, max(Length(poly[p]-poly[i]),Length(poly[p]-poly[i+1])) );
        }
    }
    旋转卡壳
    //最小圆覆盖
    inline Point Roatate_90_Angle(Point A) { return Point(A.y,-A.x); }
    struct Circle {//定义圆
        Point O; db r;
        Circle (Point a,db b=0) { O=a,r=b; }
        inline bool Is_Out_Of_Circle(Point G) { return r*r<Dot(O-G,O-G); }
    };
    Circle Get_Circle(Point A,Point B,Point C)//由三点确定一个圆
    {
        Line p1=Line((A+B)*0.5,Roatate_90_Angle(B-A));
        Line p2=Line((B+C)*0.5,Roatate_90_Angle(B-C));
        Point O=Intersection(p1,p2);
        return Circle(O,sqrt(Dot(O-A,O-A)));
    }
    void Get_Minest_Cricle(Point *P)//主过程
    {
        random_shuffle(P+1,P+n+1);
        for(int i=1;i<=n;i++)
        {
            if(!C.Is_Out_Of_Circle(P[i])) continue;
            C=Circle(P[i],0);
            for(int j=1;j<i;j++)
            {
                if(!C.Is_Out_Of_Circle(P[j])) continue;
                C=Circle( (P[i]+P[j])*0.5 , sqrt(Dot(P[i]-P[j],P[i]-P[j]))*0.5 );
                for(int k=1;k<j;k++)
                    if(C.Is_Out_Of_Circle(P[k]))
                        C=Get_Circle(P[i],P[j],P[k]);
            }
        }
    }
    最小圆覆盖

    欧拉函数

    $varphi (n)=ncdot frac{prod_{i=1}^{k}P_i-1 }{prod_{i=1}^{k}P_i }$

     证明过程:把 $n$ 唯一分解可得:

    $varphi (n)=varphi (prod _{i=1}^kprod _{j=1}^{a_i}P_i)$

     因为如果A,B互质,$varphi (AB)=varphi (A)varphi (B)$

    所以

    $varphi (prod _{i=1}^kprod _{j=1}^{a_i}P_i) =prod _{i=1}^{k}varphi(P_i^{a_i})$

     因为 $varphi(P^a)=(P-1)cdot P^{a-1}$(可以参考欧拉筛的过程)

    所以

    $prod _{i=1}^kvarphi(P_i^{a_i}) =prod _{i=1}^k((P_i-1)P^{a_i-1})=frac{prod _{i=1}^k((P_i-1)P_{i}^{a_i})}{prod _{i=1}^kP_i}$

     把分子稍微拆开一下

     $frac{prod _{i=1}^k((P_i-1)P_{i}^{a_i})}{prod _{i=1}^kP_i}=frac{prod _{i=1}^k(P_i-1)prod _{i=1}^kP_{i}^{a_i}}{prod _{i=1}^{k}P_i}$

     然后发现分子后面一部分就是 $n$,所以原式就是

     $frac{prod _{i=1}^k(P_i-1)cdot n}{prod _{i=1}^{k}P_i}$

    $varphi (n)=ncdot frac{prod_{i=1}^{k}P_i-1 }{prod_{i=1}^{k}P_i }$

     然后根据这个式子我们可以推出:

     $varphi (xy)=frac{varphi(x)varphi(y)d }{varphi(d) } ,d=gcd(x,y)$

    (待续)

  • 相关阅读:
    MySQL Workbench的使用教程 (初级入门版)
    优化MySQL语句的十个建议
    Openfire+Spark+Spark Web安装配置(一)
    agsxmpp官方源代码
    (转载)Oracle中删除外键约束、禁用约束、启用约束
    8.手工备份恢复备用数据库(练习10、11)
    (转载)图文推荐给开发人员非常实用的站点
    13.服务器管理恢复RMAN备份(练习20)
    9.手工备份恢复表空间时间点恢复(练习12.13.14)
    12.服务器管理恢复RMAN配置(练习19)
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11401457.html
Copyright © 2020-2023  润新知