zoukankan      html  css  js  c++  java
  • Note_4.7

    2019/4/7 奇奇怪怪的笔记





    狄利克雷卷积 

    • (μ∗1=ϵ),莫比乌斯反演

    • (Id=φ∗1⇒φ=μ∗Id)

    • (d=1∗1⇒1=μ∗d)

    • (σ=Id∗1⇒Id=μ∗σ)

    • (σ=φ∗d)





    一些性质?

    • (gcd(a^k-1,b^k-1)=x^{gcd(a,b)}-1)

    • (alpha^{phi(p)}≡1(mod p))

    • (gcd(Fib(a),Fib(b))=Fib(gcd(a,b)))

    • $n|m Leftrightarrow Fib_n|Fib_m $

    • (d(ij)=∑_{p|i}∑_{q|j}[gcd(p,q)=1])

    • (σ_0(n_1n_2...n_m)=sum_{a_1|n_1}sum_{a_2|n_2}...sum_{a_m|n_m}prod_{1leq i <jleq m} [gcd(a_i,a_j)=1 ])

    • (σ(ij)=∑_{p|i}∑_{q|j}[gcd(p,q)=1]frac{i}{p}q)

    • (∑^n_{i=1}μ^2(i)=∑_{i⩾1}μ(i)⌊frac{n}{i^2}⌋)





    广义容斥原理



    二项式反演

    • (b_k=sum_{i=0}^kinom{k}{i}a_i \Rightarrow a_k=sum_{i=0}^k(-1)^{k-i}inom{k}{i}b_i)
    • (b_k=sum_{i=k}^n inom{i}{k}a_i\Rightarrow a_k=sum_{i=k}^n(-1)^{i-k}inom{i}{k}b_i)

    简单证明:

    (inom{n}{i}inom{i}{j}=inom{n}{j}inom{n-j}{i-j})

    对于一式

    (sum_{i=0}^k(-1)^{k-i}inom{k}{i}b_i=sum_{j=0}^n a_j (sum_{i=j}^k(-1)^{k-i}inom{k}{j}inom{k-j}{i-j}))

    考虑二项式定理:

    (sum_{i=j}^{k}(-1)^{k-i}inom{k-j}{i-j}=[k-j=0])

    对于二式

    (sum_{i=k}^n(-1)^{i-k}inom{i}{k}b_i=sum_{j=k}^{n} a_j (sum_{i=k}^{j}(-1)^{i-k}inom{i}{k}inom{j}{i}))

    (sum_{i=k}^{j}(-1)^{i-k}inom{i}{k}inom{j}{i}=inom{j}{k}sum_{i=k}^{j}(-1)^{i-k}inom{j-k}{i-k}=[j=k])

    核心是二项式定理,然后记忆方法就是考虑上式和下式其实之差一个((-1)^{i-k})



    广义容斥原理

    (A_i)表示含有性质(i)的元素集合

    (b_m)表示恰好含有(m)种性质的元素个数

    (a_m)表示至少含有(m)种性质的元素的元次

    [a_m=sum_{Iin C(n,m)}|igcap_{iin I}^{} A_i| ]

    发现,含有(m+k)个性质的元素被(a_m)记录了(inom{m+k}{m})

    所以(a_m=sum_{i=m}^{n} b_iinom{i}{m}),根据二项式反演





    Prufer序列与Cayley公式

    Prufer序列

    与无根树一一对应

    定义无根树中度数为(1)的点是叶子节点。

    求Prufer序列

    找到编号最小的叶子节点(x),在Prufer序列中添加与(x)相连的点,然后删除(x)

    不断重复,直到只剩下(2)个节点。

    具体可以用一个(set),实时维护度数为(1)的点。

    求无根树

    记点集为(V),每次取出Prufer序列第一个元素(x),然后在(V)中找到最小的没有在Prufer序列中出现的元素(y),在(x)(y)之间连一条边,然后在Prufer序列中删除(x),点集(V)中删除(y),不断重复,直到(V)中剩下两个点,最后在这两个点中连一条边。

    具体也可以用一个(set),实时维护Prufer序列中没有出现过的点。



    Cayley公式

    (n)个点有标号的无根树的个数:(n^{n−2})

    度数为(d_i)的点在序列中出现了(d_i)

    (n)个点,第(i)个点的度数为(d_i)的无根树个数:(frac{(n−2)!}{∏^{n}_{i=1}(d_i−1)!})



    千年不写的Treap

    Code

    #define lc c[x][0]
    #define rc c[x][1]
    int c[MN][2],siz[MN],cnt[MN],rnd[MN],val[MN];
    int sz,rt;
    inline void update(int x){siz[x]=cnt[x]+siz[lc]+siz[rc];}
    inline void lturn(int &x)//旋右儿子
    {
        int ch=rc;rc=c[ch][0];c[ch][0]=x;
        siz[ch]=siz[x];update(x);x=ch;
    }
    inline void rturn(int &x)//旋左儿子
    {
        int ch=lc;lc=c[ch][1];c[ch][1]=x;
        siz[ch]=siz[x];update(x);x=ch;
    }
    void Ins(int &x,int v)
    {
        //注意点,siz是在递归前加的,然后每一步都要考虑是否满足堆的性质
        if(!x){x=++sz;lc=rc=0;siz[x]=cnt[x]=1;val[x]=v;rnd[x]=rand();return;}
        siz[x]++;if(val[x]==v){cnt[x]++;return;}
        if(v<val[x]){Ins(lc,v);if(rnd[lc]<rnd[x]) rturn(x);}
        else {Ins(rc,v);if(rnd[rc]<rnd[x]) lturn(x);}
    }
    int Rnk(int x,int v)
    {
        //查询v值在x子树内的排名
        if(val[x]==v) return siz[lc]+1;
        if(val[x]>v) return Rnk(lc,v);
        else return siz[lc]+cnt[x]+Rnk(rc,v);
    }
    int Kth(int x,int k)
    {
        //查询x子树内排名为k的节点的值
        if(k<=siz[lc]) return Kth(lc,k);
        else if(k>siz[lc]+cnt[x]) return Kth(rc,k-siz[lc]-cnt[x]);
        else return val[x];
    }
    void Del(int &x,int v)
    {
        //找到点后,把它旋到底(或只有一个孩子),然后删除
        if(!x) return;
        if(val[x]==v)
        {
            if(cnt[x]>1){cnt[x]--;siz[x]--;return;}
            if(lc*rc==0) x=lc+rc;
            else if(rnd[lc]<rnd[rc]) rturn(x),Del(x,v);
            else lturn(x),Del(x,v);
        }
        else siz[x]--,Del(v>val[x]?rc:lc,v);
    }
    int Pre(int x,int v)
    {
        //查询v在x子树内的前驱
        if(!x) return 0;int q;
        if(val[x]<v) return (q=Pre(rc,v))?q:x;
        else return Pre(lc,v);
    }
    int Suf(int x,int v)
    {
        //查询v在x子树内的后缀
        if(!x) return 0;int q;
        if(val[x]>v) return (q=Suf(lc,v))?q:x;
        else return Suf(rc,v);
    }
    





    超级可爱的Splay

    Code

    #define rtf 400004
    #define rt c[rtf][0]
    //这样定义就可以解决rt在rtt中的更新问题
    using namespace std;
    int n,sz[MN],c[MN][2],tn,fa[MN],val[MN],sum[MN],cnt;
    void update(int x){sz[x]=sz[c[x][0]]+sz[c[x][1]]+sum[x];}
    void rotate(int x)
    {
        int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;fa[y]=x,fa[x]=z,fa[c[x][r]]=y;
        c[z][c[z][1]==f]=x,c[y][l]=c[x][r],c[x][r]=y;update(y);
    }
    void splay(int x,int y)
    {
        //旋转到y的子节点
        for(int f;fa[x]!=y;rotate(x))
            if(fa[f=fa[x]]!=y)rotate(c[fa[f]][1]==f^c[f][1]==x?x:f);
        update(x);
    }
    void ins(int f,int ty,int v)
    {
        //这里的当前节点就是c[f][ty],在递归前就要更新siz,最后要splay一下
        if(!c[f][ty]){fa[c[f][ty]=++tn]=f;val[tn]=v;sum[tn]=1;splay(tn,rtf);return;}
        if(v==val[c[f][ty]]){sum[c[f][ty]]++;splay(c[f][ty],rtf);return;}
        ++sz[f=c[f][ty]];
        ins(f,v>val[f],v);
    }
    void del(int f,int ty,int v)
    {
        //当前点就是c[f][ty],最后要Splay一下
        //把c[f][ty]旋到根,把后继旋到它的右儿子,然后把根的左儿子继承给右儿子
        //同样在递归前修改siz
        if(val[c[f][ty]]==v)
        {
            int x=c[f][ty];
            if(sum[x]>1){sum[x]--,splay(x,rtf);return;}int tmp;
            if(!(c[x][0]*c[x][1])){c[f][ty]=c[x][0]+c[x][1],fa[c[x][c[x][1]!=0]]=f;return;}
            for(tmp=c[x][1];c[tmp][0]!=0;tmp=c[tmp][0]);
            splay(x,rtf),splay(tmp,rt);
            c[tmp][0]=c[x][0],rt=tmp,fa[c[x][0]]=tmp,fa[tmp]=rtf;update(tmp);return;
        }
        --sz[f=c[f][ty]];del(f,v>val[f],v);
    }
    int findk(int x,int k)
    {
        //查询x子树内第k大的节点
        if(k<=sz[c[x][0]])return findk(c[x][0],k);
        else if(k>sz[c[x][0]]&&k<=sz[c[x][0]]+sum[x]){splay(x,rtf);return val[x];}
        else return findk(c[x][1],k-=sz[c[x][0]]+sum[x]);
    }
    int getk(int x,int k)
    {
        //查询x子树内小于权值k的节点数量
        if(!x)return 0;
        if(val[x]>=k)return getk(c[x][0],k);
        else return sz[c[x][0]]+sum[x]+getk(c[x][1],k); 
    }
    int pre(int x){int tmp=getk(rt,x);return findk(rt,tmp);}
    int suf(int x){int tmp=getk(rt,x+1)+1;return findk(rt,tmp);}
    





    写起来最舒服的FHQ

    Code

    //只要会三个函数:random,split,merge
    class fhq
    {
        #define MN 100005
        private:
            int sz;
            int val[MN],pri[MN],ls[MN],rs[MN],siz[MN],cnt;
            inline unsigned int random()
            {
                static unsigned int x=23333;
                return x^=x<<13,x^=x>>17,x^=x<<5;
            }
            inline void combine(int x){siz[x]=1+siz[ls[x]]+siz[rs[x]];}
        public:
            int rt;
            int Merge(int rt1,int rt2)
            {
                if(!rt1||!rt2) return rt2+rt1;
                if(pri[rt1]<pri[rt2])
                {
            		rs[rt1]=Merge(rs[rt1],rt2);
            		combine(rt1);return rt1;
        		}
        		else
                {
            		ls[rt2]=Merge(rt1,ls[rt2]);
            		combine(rt2);return rt2;
        		}
            }
        	void Split(int x,int k,int &rt1,int &rt2)
            {
                if(!x)return(void)(rt1=rt2=0);
                if(k<=siz[ls[x]]) Split(ls[x],k,rt1,ls[x]),combine(x),rt2=x;
                else Split(rs[x],k-siz[ls[x]]-1,rs[x],rt2),combine(x),rt1=x;
            }
            int Rank(int x,int v)
            {
                if(!x) return 0;
                if(v<val[x]) return Rank(ls[x],v);
                else return siz[ls[x]]+Rank(rs[x],v)+1;
            }
            int Kth(int k)
            {
                register int rt1,rt2,rt3,c;
                Split(rt,k,rt1,rt2);Split(rt1,k-1,rt3,c);
                rt=Merge(rt3,Merge(c,rt2));
                return val[c];
            }
            void Insert(int v)
            {
                val[++sz]=v;pri[sz]=random(),siz[sz]=1;
                register int rk=Rank(rt,v),rt1,rt2;
                Split(rt,rk,rt1,rt2);
                rt=Merge(Merge(rt1,sz),rt2);
            }
            void Delete(int v)
            {
                register int rk=Rank(rt,v),rt1,rt2,rt3,c;
                Split(rt,rk,rt1,rt2);Split(rt1,rk-1,rt3,c);
                rt=Merge(rt3,rt2);
            }
    }T;
    





    可持久化FHQ

    Code

    int NewNode(int x)
    {
        ++sz;
        val[sz]=val[x];pri[sz]=pri[x];ls[sz]=ls[x];
        rs[sz]=rs[x];siz[sz]=siz[x];
        return sz;
    }
    int Merge(int rt1,int rt2)
    {
        if(!rt1||!rt2) return rt2|rt1;
        if(pri[rt1]<pri[rt2])
        {
            register int p=NewNode(rt1);
            rs[p]=Merge(rs[rt1],rt2);
            combine(p);return p;
        }
        else
        {
            register int p=NewNode(rt2);
            ls[p]=Merge(rt1,ls[rt2]);
            combine(p);return p;
        }
    }
    void Split(int x,int k,int&rt1,int&rt2)
    {
        if(!x) return (void)(rt1=rt2=0);
        if(k<=siz[ls[x]])
        {
            rt2=NewNode(x);
            Split(ls[x],k,rt1,ls[rt2]);
            combine(rt2);
        }
        else
        {
            rt1=NewNode(x);
            Split(rs[x],k-siz[ls[x]]-1,rs[rt1],rt2);
            combine(rt1);
        }
    }
    





    Link_Cut_Tree

    大部分时候都是有根树,也只查询到根的路径,所以根本不需要换根,但是还是有必要会写(mkrt)

    Code

    class Link_Cut_Tree
    {
        #define MN 300005 
        private:
            int N,fa[MN],c[MN][2],st[MN],val[MN],X[MN];
            bool rev[MN];
            inline bool nrt(int x){return c[fa[x]][0]==x||c[fa[x]][1]==x;}
            inline void Rev(int x){rev[x]^=1;std::swap(c[x][0],c[x][1]);}
            inline void up(int x){X[x]=X[c[x][0]]^X[c[x][1]]^val[x];}
            inline void down(int x){if(x&&rev[x])Rev(c[x][0]),Rev(c[x][1]),rev[x]=0;}
            #define get(x) (c[fa[x]][1]==x)
            inline void rotate(int x)
            {
                int y=fa[x],z=fa[y],l=get(x),r=l^1;if(nrt(y))c[z][get(y)]=x;fa[x]=z;
                c[y][l]=c[x][r];fa[c[x][r]]=y;c[x][r]=y;fa[y]=x;up(y);
            }
            inline void Splay(int x)
            {
                //最容易写错的部分!
                static int top,q[MN];q[top=1]=x;register int i;
                for(i=x;nrt(i);i=fa[i]) q[++top]=fa[i];for(;top;--top) down(q[top]);
                for(;nrt(x);rotate(x))if(nrt(fa[x])) rotate(get(fa[x])^get(x)?x:fa[x]);up(x);
            }
            #undef get
        //acs的时候一定要up(x)!
            inline void access(int x){register int i;for(i=0;x;x=fa[i=x])Splay(x),c[x][1]=i,up(x);}
            inline void mkrt(int x){access(x);Splay(x);Rev(x);}
        //fdrt后一定要Splay一下!还有就是找根的时候要pushdown
            inline int fdrt(int x){access(x),Splay(x);for(;c[x][0];down(c[x][0]),x=c[x][0]);Splay(x);return x;}
            inline void Split(int x,int y){mkrt(x);access(y);Splay(y);}
        public:
            inline void init(int n){register int i;for(i=1;i<=n;++i) val[i]=X[i]=read();}
        //如果是有根树:直接fa[x]=y
            void Link(int x,int y){mkrt(x);if(fdrt(y)!=x)fa[x]=y;}
        //如果是有根树:acs(x);Splay(x);fa[c[x][0]]=0;c[x][0]=0;up(x);
            void Cut(int x,int y){Split(x,y);if(c[y][0]==x&&!c[x][1])c[y][0]=fa[x]=0,up(y);}
            inline int Query(int x,int y){if(x==y) return val[x];Split(x,y);return X[y];}
            inline void Modify(int x,int V){Splay(x);val[x]=V;up(x);}
        #undef MN
    }T;
    






    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    golangWEB框架gin学习之路由群组
    golangWEB框架gin学习之获取post参数
    golangWEB框架gin学习之获取get参数
    golang之bufio包的使用
    linux配置java环境变量
    jmeter接口测试实践
    Selenium2(java)框架设计 九
    Selenium2(java)页面对象模型(Page Object) 八
    Selenium2(java)TestNG的使用 七
    Selenium2(java)selenium常用API 六
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/10667785.html
Copyright © 2011-2022 走看看