• 模板【树链剖分】


    PART1(算法思想简介)

    1.实现

    模板例题

    2.时间复杂度

    3.特别优势

    4.适用情况

    5.需要注意的点

    6.函数、变量名的解释+英文

    7.dalao分析

    讲得很好

    基本实现

    每棵子树 x 在 DFS 序列中一定是连续的一段,结点 x 一定在这段的开头。这使得在子树上进行的修改、查询可以转化为区间修改、区间查询。结合树状数组 or 线段树食用均可。

    PART2(算法各种类型(并附上代码))

     实现

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<queue>
    #include<iomanip>
    #include<iostream>
    #include<stack>
    using namespace std;
    //-------------------------------------------这样的线是分割线
    //-------------------------------------------常用规定部分:都采用‘小写字母’+‘_’的方式
    #define inf_ 0x3f3f3f3f
    #define reg_ register int
    #define for_reg(i, n) for(reg_ i = 1; i <= n; i++)
    //----------------------边访问
    #define visit_edge int i = p[u]; ~i; i = e[i].next
    #define define_v int v = e[i].v
    #define define_v_avoid_f int v = e[i].v;if(v == fa) continue
    //----------------------线段树
    #define mid_ int mid = (l+r)>>1//mid的定义
    #define len_ (r-l+1)//像这样的式子千万要打括号,要不然就完了
    #define l_id id<<1
    #define r_id id<<1|1
    #define l_son id<<1,l,mid
    #define r_son id<<1|1,mid+1,r
    #define include_(x,y,l,r) x<=l && r<=y
    const int max_n = 1e5+10;
    const int max_m = 2e5+10;
    //-----------------------------------------------变量声明部分:全局变量除第一个单词外其它单词首字母大写+结尾加一个g(global variable)
    //-----------------------与边相关
    struct edge
    {
        int u, v, next, w;
    } e[max_m];
    int p[max_n], eid;
    //------------------------其它
    int ng, mg, rg, modg;//cin
    int segmentg[max_n<<2], lazyg[max_n<<2];//线段树
    int fag[max_n], depg[max_n], sizeg[max_n], song[max_n], idg[max_n], topg[max_n],  cntg;//son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
    int resg;//Query_BinaryIndexedTree()记录答案
    int valueg[max_n];
    int valueTmpg[max_n];
    //-------------------------------------------------函数声明部分:函数的单词首字母大写,常用局部变量简化
    inline int read()
    {
        int x=0;
        char c=getchar();
        while(c<'0'||c>'9')
            c=getchar();
        while(c>='0'&&c<='9')
        {
            x=(x<<1)+(x<<3)+c-'0';
            c=getchar();
        }
        return x;
    }
    //---------------------------建图
    inline void InitEdge()
    {
        memset(p, -1, sizeof(p));
        eid = 0;
    }
    inline void Insert(int u, int v, int w = 0)
    {
        e[eid].next = p[u];
        e[eid].u = u;
        e[eid].v = v;
        e[eid].w = w;
        p[u] = eid++;
    }
    //----------线段树
    inline void PushDown_BinaryIndexedTree(int id, int len)
    {
        if(lazyg[id])
        {
            lazyg[l_id] += lazyg[id];
            lazyg[r_id] += lazyg[id];
            segmentg[l_id] += lazyg[id]*(len-(len>>1));//值得留意一下,别写反了
            segmentg[r_id] += lazyg[id]*(len>>1);
            segmentg[l_id] %= modg;
            segmentg[r_id] %= modg;
            lazyg[id] = 0;
        }
    }
    inline void PushUp_BinaryIndexedTree(int id)
    {
        segmentg[id] = (segmentg[l_id]+segmentg[r_id]) % modg;
    }
    inline void Build_BinaryIndexedTree(int id, int l, int r)
    {
        if(l == r)
        {
            segmentg[id] = valueg[l] % modg;
            return ;
        }
        mid_;
        Build_BinaryIndexedTree(l_son);
        Build_BinaryIndexedTree(r_son);
        PushUp_BinaryIndexedTree(id);
    }
    inline void Query_BinaryIndexedTree(int id, int l, int r, int x, int y)
    {
        if(include_(x,y,l,r))
        {
            resg += segmentg[id], resg %= modg;
            return ;
        }
        PushDown_BinaryIndexedTree(id, len_);
        mid_;
        if(x<=mid)
            Query_BinaryIndexedTree(l_son, x, y);
        if(mid<y)
            Query_BinaryIndexedTree(r_son, x, y);
        PushUp_BinaryIndexedTree(id);
    }
    inline void UseQuery_BinaryIndexedTree(int id, int l, int r, int x, int y)
    {
        resg = 0;
        Query_BinaryIndexedTree(id, l, r, x, y);
    }
    inline void UpDate_BinaryIndexedTree(int id, int l, int r, int x, int y, int k)
    {
        if(include_(x,y,l,r))
        {
            lazyg[id] += k;
            segmentg[id] += k*len_;
            return ;
        }
        PushDown_BinaryIndexedTree(id, len_);
        mid_;
        if(x<=mid)
            UpDate_BinaryIndexedTree(l_son, x, y, k);
        if(mid<y)
            UpDate_BinaryIndexedTree(r_son, x, y, k);
        PushUp_BinaryIndexedTree(id);
    }
    //---------树链剖分准备
    //求出depg,sizeg,fag,song几个数组
    //u当前节点,fa父亲,deep深度(初始传入(root,root's father,1))
    inline void Dfs1_LinkCutTree(int u,int fa,int deep) //x当前节点,f父亲,deep深度
    {
        depg[u]=deep;
        fag[u]=fa;
        sizeg[u]=1;
        int maxSon=-1;//记录重儿子的儿子数
        for(visit_edge)
        {
            define_v_avoid_f;
            Dfs1_LinkCutTree(v, u, deep+1);
            sizeg[u] += sizeg[v];
            if(sizeg[v] > maxSon)
                song[u] = v, maxSon = sizeg[v]; //标记每个非叶子节点的重儿子编号
        }
    }
    //求出id,top,给新的标号赋新valueg
    //u当前节点,top当前链的最顶端的节点(初始传入(root, root)(root所在链顶就是root))
    inline void Dfs2_LinkCutTree(int u,int top) 
    {
        idg[u]=++cntg;//标记每个点的新编号
        valueg[cntg] = valueTmpg[u];//把每个点的初始值赋到新编号上来
        topg[u] = top;//这个点所在链的顶端
        if(song[u])
            Dfs2_LinkCutTree(song[u],top);//按先处理重儿子,再处理轻儿子的顺序递归处理
        for(visit_edge)
        {
            define_v;
            if(v == fag[u] || v == song[u])
                continue;
            Dfs2_LinkCutTree(v, v);//对于每一个轻儿子都有一条从它自己开始的链
        }
    }
    //----------树链剖分操作
    //求树上x到y的和(包括这两个点)
    inline int QRange_LinkCutTree(int x, int y)
    {
        int ans=0;
        while(topg[x]!=topg[y]) //当两个点不在同一条链上
        {
            if(depg[topg[x]]<depg[topg[y]])
                swap(x,y);//把x点改为所在链顶端的深度更深的那个点
            UseQuery_BinaryIndexedTree(1,1,ng,idg[topg[x]],idg[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
            ans+=resg;
            ans%=modg;//按题意取模
            x=fag[topg[x]];//把x跳到x所在链顶端的那个点的上面一个点
        }
        //直到两个点处于一条链上
        if(depg[x]>depg[y])
            swap(x,y);//使得x的dfs序更小
        UseQuery_BinaryIndexedTree(1,1,ng,idg[x],idg[y]);//这时再加上此时两个点的区间和即可(包括这两个点)
        ans+=resg;
        return ans%modg;
    }
    //更新树上x到y的数据,都加k
    inline void UpDateRange_LinkCutTree(int x,int y,int k) 
    {
        k %= modg;
        while(topg[x]!=topg[y])
        {
            if(depg[topg[x]]<depg[topg[y]])
                swap(x,y);
            UpDate_BinaryIndexedTree(1,1,ng,idg[topg[x]],idg[x],k);
            x=fag[topg[x]];
        }
        if(depg[x]>depg[y])
            swap(x,y);
        UpDate_BinaryIndexedTree(1,1,ng,idg[x],idg[y],k);
    }
    //求以x为根节点的树的和
    inline int QSon_LinkCutTree(int x)
    {
        UseQuery_BinaryIndexedTree(1,1,ng,idg[x],idg[x]+sizeg[x]-1);//子树区间右端点为id[x]+siz[x]-1
        return resg;
    }
    //更新以x为根节点的树的数据,全部加k
    inline void UpDateSon_LinkCutTree(int x,int k) 
    {
        UpDate_BinaryIndexedTree(1,1,ng,idg[x],idg[x]+sizeg[x]-1,k);
    }
    int main()
    {
        //freopen("in.txt","r", stdin);
        //freopen("out.txt","w", stdout);
        ios::sync_with_stdio(false);
        InitEdge();
        cin >> ng >> mg >> rg >> modg;
        for_reg(i, ng) cin >> valueTmpg[i];
        int u, v;
        for_reg(i, ng-1)
        {
            cin >> u >> v;
            Insert(u, v);
            Insert(v, u);
        }
    
        Dfs1_LinkCutTree(rg, 0, 1);
        cntg = 0;
        Dfs2_LinkCutTree(rg, rg);
    
        Build_BinaryIndexedTree(1, 1, ng);
        
        while(mg--)
        {
            int x, y, z, k;
            cin >> k;
            if(k == 1)
            {
                cin >> x >> y >> z;
                UpDateRange_LinkCutTree(x, y, z);
            }
            else if(k == 2)
            {
                cin >> x >>y;
                cout << QRange_LinkCutTree(x, y) << endl;
            }
            else if(k == 3)
            {
                cin >> x >> y;
                UpDateSon_LinkCutTree(x, y);
            }
            else
            {
                cin >> x;
                cout << QSon_LinkCutTree(x) << endl;
            }
        }
        return 0;
    }
    View Code

    PART3(算法的延伸应用)

    PART4(对算法深度的理解)

    PART5(与其相关的有趣题目)

     

  • 相关阅读:
    Asp.NetCore3.1 WebApi 获取配置json文件中的数据
    Dapper同时操作任意多张表的实现
    将视图批量新增到PowerDesigner中并以model图表的形式展示
    .NetCore3.1获取文件并重新命名以及大批量更新及写入数据
    .NetCore 简单的使用中间件
    比较复杂的SQL转Linq
    Asp.NetCore3.1版本的CodeFirst与经典的三层架构与AutoFac批量注入
    Git与GitLab的分支合并等简单的测试操作
    Winform的控件以及DataGridView的一般使用
    在Linux系统中运行并简单的测试RabbitMq容器
  • 原文地址:https://www.cnblogs.com/bear-xin/p/14974128.html
Copyright © 2020-2023  润新知