树链剖分

淺唱寂寞╮ 提交于 2020-01-26 17:08:07

定义:树链(树上的路径),剖分(把路径分成重链和轻链);

用途:在一棵树上进行修改、求极值、求和。

时间复杂度:,数据范围100,000;

概念:

  • 重儿子:节点u所有的儿子中,siz最大的儿子;

  • 轻儿子:节点u所有的非重儿子的儿子;

  • 重链:所有连续重儿子连接成的链;

  • 轻链(轻边):由节点u跟轻儿子连接的边,最多包含两个定点。

结论:

  • 所有的重链个数不超过.

  • 所有的轻边的个数不超过;

  • 轻边的size*2<SIZE;

维护的数据

  • val[u] 节点u的价值

  • tid[u] 节点u的新编号,用于维护线段树

  • valt[u] 节点u的新编号对应的价值,用于维护线段树

  • size[u] 以u为根的子树的节点数; 用于区分重儿子和轻儿子。

  • dep[u] 节点v的深度,根节点的深度为1或0;

  • top[u] 节点u所在链的顶端节点,(u,v)如果为重链,top[u]==top[v],轻链或者轻链重链的结合,top[u]!=top[v].

  • fa[u] 表示u节点的额父亲节点

实现过程:

  • 邻接表存树

  • DFS1,维护出fa[],dep[]和siz[]

  • DFS2,优先访问重儿子,维护出tid[],valt[],top[]

  • 用tid[]和valt[]建立线段树。

  • 修改,修改u的tid[u]在线段树上的值即可。

  • 查询,两部查询,把查询的链分成重链和轻链查询。如果是复合链,优先查询dep[top[u]]深度深的一端。防止查询越界。

例题:BZOJ1036树的统计Count

  • 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II。QMAX u v: 询问从点u到点v的路径上的节点的最大权值。 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和。注意:从点u到点v的路径上的节点包括u和v本身。

  • 输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

  • 对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e4 + 15;
int N, Q, u, v;
//-----------------------邻接表
struct node
{
    int id, next;
} E[maxn << 1]; //双向边
int head[maxn], num;
void init()
{
    memset(head, -1, sizeof(head));
    num = 0;
}
void adde(int u, int v)
{
    E[num].id = v;
    E[num].next = head[u];
    head[u] = num++;
}
//--------------------------------DFS1/2
int val[maxn], valt[maxn], tid[maxn], siz[maxn], top[maxn],
dep[maxn], fa[maxn];
int clk;
void DFS1(int u, int p)
{
    dep[u] = dep[p] + 1;
    fa[u] = p;
    siz[u] = 1;
    for(int k = head[u]; k != -1; k = E[k].next)
    {
        int id = E[k].id;
        if(id == p) continue;
        DFS1(id, u);
        siz[u] += siz[id];
    }
}
void DFS2(int u, int p)
{
    tid[u] = ++clk;
    valt[clk] = val[u];
    int son = -1;
    for(int k = head[u]; k != -1; k = E[k].next)
    {
        int id = E[k].id;
        if(id == p)continue;
        if(son == -1 || siz[id] > siz[son]) son = id;
    }
    if(son != -1)
    {
        top[son] = top[u];
        DFS2(son, u); 
    }
    for(int k = head[u]; k != -1; k = E[k].next)
    {
        int id = E[k].id;
        if(id == p || id == son)continue;
        top[id] = id;
        DFS2(id, u);
    }
}
//--------------------------------线段树
int mx[maxn*4], sum[maxn*4];
void build(int id, int l, int r)
{
    if(l == r) mx[id] = sum[id] = valt[l];
    else
    {
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
        mx[id] = max(mx[id << 1], mx[id << 1 | 1]);
        sum[id] = sum[id << 1] + sum[id << 1 | 1];
    }
}
void update(int id, int l, int r, int u, int vual)
{
    if(l == r) mx[id] = sum[id] = vual;
    else
    {
        int mid = (l + r) >> 1;
        if(u <= mid)  update(id << 1, l, mid, u, vual);
        else update(id << 1 | 1, mid + 1, r, u, vual);
        mx[id] = max(mx[id << 1], mx[id << 1 | 1]);
        sum[id] = sum[id << 1] + sum[id << 1 | 1];
    }
}
int QMax(int id, int L, int R, int l, int r)
{
    if(R <= r && L >= l) return mx[id];
    else
    {
        int mid = (L + R) >> 1;
        int ret = -INT_MAX;
        if(l <= mid)ret = max(ret, QMax(id << 1, L, mid, l, r));
        if(r >= mid + 1) ret = max(ret, QMax(id << 1 | 1, mid + 1, R, l, r));
        return ret;
    }
}
int QMax(int u, int v)
{
    int ret = -INT_MAX;
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        ret = max(ret, QMax(1, 1, N, tid[top[u]], tid[u]));
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    ret = max(ret, QMax(1, 1, N, tid[v], tid[u]));
    return ret;
}
int Qsum(int id, int L, int R, int l, int r)
{
    if(R <= r && L >= l) return sum[id];
    else
    {
        int mid = (L + R) >> 1;
        int ret = 0;
        if(l <= mid)ret += Qsum(id << 1, L, mid, l, r);
        if(r >= mid + 1) ret += Qsum(id << 1 | 1, mid + 1, R, l, r);
        return ret;
    }
}
int Qsum(int u, int v)
{
    int ret = 0;
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        {
            ret += Qsum(1, 1, N, tid[top[u]], tid[u]);
            u = fa[top[u]];
        }
    }
    if(dep[u] < dep[v]) swap(u, v);
    ret += Qsum(1, 1, N, tid[v], tid[u]);
    return ret;
}
//--------------------------------主函数
int main ()
{
    init();
    scanf("%d",&N);
    for(int i = 1; i < N; i++)
    {
        scanf("%d %d", &u, &v);
        adde(u, v);
        adde(v, u);
    }
    for(int i = 1; i <= N; i++)
        scanf("%d", &val[i]);
    DFS1(1, 0);
    top[1] = 1;
    DFS2(1, 0);
    build(1, 1, N);
    char s[10];
    scanf("%d", &Q);
    for(int i = 1; i <= Q; i++)
    {
        scanf("%s%d%d", s, &u, &v);
        if(!strcmp(s, "QMAX"))
            printf("%d\n", QMax(u, v));
        else  if(!strcmp(s, "QSUM"))
            printf("%d\n",Qsum(u, v));
        else
        {
            update(1, 1, N, tid[u], v);
        }
    }
    return 0;
}

  

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!