关于OI中的各种数学

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

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

一些符号:

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

$\epsilon $ 叫单位元,对每个 $f(1) \neq 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)^k\binom{n}{k}=[n=0]$,证明可以通过递推式,数学归纳法解决

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

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

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

证明:

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

$f(n)=\sum_{m=0}^{n}\sum_{k=0}^{n-m}(-1)^k\binom{n-m}{k}\binom{n}{m}f(m)$

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

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

$f(n)=\sum_{k=0}^{n}(-1)^k\binom{n}{k}\sum_{m=0}^{n-k}\binom{n-k}{m}f(m)$

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

$f(n)=\sum_{k=0}^{n}(-1)^{n-k}\binom{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} \right \rfloor)$

证明:

$\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} \right \rfloor}f(j)$

$= \sum_{d=1}^{n} g(d)S_{f}(\left \lfloor \frac{n}{d} \right \rfloor)$

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

 

因为 $\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} \right \rfloor)$

$S_{\mu}(n)=1-\sum_{i=2}^{n}S_{\mu}(\left \lfloor \frac{n}{i} \right \rfloor)$

 

因为 $\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} \right \rfloor)$

$S_{\phi}(n)=\frac {n(n+1)} {2} - \sum_{i=2}^{n}S_{\phi}(\left \lfloor \frac{n}{i} \right \rfloor)$

 

$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})=n\sum_{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} \right \rfloor)$

 

生成函数相关:

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

$\sum_{n=0}^{\infty }\binom{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  \right |}\sum_{i=1}^{n}\left | Z_i \right |=\frac{1} {\left |G  \right |}\sum_{\pi \in G}C(\pi)$

$Polya$ 定理: $ L= \frac {1} {\left |G  \right |} (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)=n\cdot \frac{\prod_{i=1}^{k}P_i-1 }{\prod_{i=1}^{k}P_i }$

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

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

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

所以

$\varphi (\prod _{i=1}^k\prod _{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}^k\varphi(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)=n\cdot \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)$

(待续)

posted @ 2019-08-23 17:25  LLTYYC  阅读(475)  评论(0编辑  收藏  举报