华华和月月种树

华华和月月种树

知识点

树剖+树状数组

思路

容我吐槽一下,这牛客LCA题单全都是点树剖,真不如叫树剖题单(doge)

好啦,我们正式来说这题。

我们先正常分析,遇见问题我们在过来调整思路。

首先,我们不考虑第一个操作,来看剩下两个。

这里面,首先我们看到了一个操作。

操作 2:输入格式 2 i a,表示华华上线做任务使节点 i 的子树中所有节点(即它和它的所有子孙节点)权值加 a 。

其实如果只是这个操作的话,我们可以考虑维护一个树上差分数组sum,这样我们就可以把区间操作变成单点操作,每次只需要对根节点+a,每个节点到根节点的路径的和就是在该点的权值。

但是我们发现还有单点询问的操作。

询问 3:输入格式3 i,华华需要给出 i 节点此时的权值。

此时可以发现,我们要进行的操作就是,动态的进行单点修改和区间查询,这我们立马就想到了树状数组。为了利用起来树状数组,我们要加一个树剖。

现在我们来看看操作1

操作 1:输入格式1 i,表示月月氪金使节点 i 长出了一个新的儿子节点,权值为0,编号为当前最大编号 +1(也可以理解为,当前是第几个操作 1,新节点的编号就是多少)。

这一开始令我非常苦恼,因为如果树会动态变化的话,那我们就没办法建立起来树剖。

最后,我想到了一个还算比较巧妙的做法。

做法

  • 首先,我们将所有操作存下来,其中我们依据给的父子关系,将树建立起来。
  • 接着,我们根据树将树剖建立起来
  • 然后,我们再顺序的执行三个操作。
    • 对于操作2,我们直接将根节点下面的子树节点全部+a(包括此时还没有创立出来的)
    • 对于操作1,此时i下边的u才刚刚被建立出来,则我们用一个数组ans记录之前被多加的部分。
    • 对于询问3,此时对于u节点来说,其本身的值应当为u节点到根节点的路径权值和-ans[u]

Ac_code

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10,M = N<<1;
struct Node
{
    int op,u,a;
}OP[N];
int h[N],e[N],ne[N],idx;
int fa[N],son[N],sz[N];
int dfn[N],ts;
int ans[N],cnt;
int tr[N];
int n,id;

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void insert(int x,int c)
{
    while(x<=n)
    {
        tr[x] += c;
        x += x & -x;
    }
}

int sum(int x)
{
    int res = 0;
    while(x)
    {
        res += tr[x];
        x -= x & -x;
    }
    return res;
}

void dfs1(int u)
{
    sz[u] = 1;
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        dfs1(j);
        sz[u] += sz[j];
        if(sz[j]>sz[son[u]]) son[u] = j;
    }
}

void dfs2(int u)
{
    dfn[u] = ++ts;
    if(!son[u]) return ;
    dfs2(son[u]);
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        if(j==son[u]) continue;
        dfs2(j);
    }
}

int main()
{
    scanf("%d",&n);
    memset(h,-1,sizeof h);
    id = 1;
    for(int i=0;i<n;i++)
    {
        int op,u,a;scanf("%d%d",&op,&u);
        u++;
        if(op==2) scanf("%d",&a);
        if(op==1) add(u,++id);
        if(op!=1) OP[i] = {op,u,a};    
        else OP[i] = {op,id};
    }
    dfs1(1);dfs2(1);
    for(int i=0;i<n;i++)
    {
        int op = OP[i].op,u = OP[i].u,a = OP[i].a;
        if(op==1) ans[u] = sum(dfn[u]);
        if(op==2) insert(dfn[u],a),insert(dfn[u]+sz[u],-a);
        if(op==3) cout<<sum(dfn[u])-ans[u]<<"\n";
    }
    return 0;
}
posted @ 2022-06-30 10:39  艾特玖  阅读(37)  评论(0)    收藏  举报