模板【树链剖分】

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(与其相关的有趣题目)

 

posted @ 2021-07-05 19:55  bear_xin  阅读(34)  评论(0)    收藏  举报