【每日一题】华华和月月种树

华华和月月种树

https://ac.nowcoder.com/acm/problem/23051

题意:




思路:

离线操作,把所有操作存起来,对于所有1操作建好一颗最终的树就变成了裸的树链剖分
但是可能会出现,一个点还没建出来就已经被加了权值的错误
因此对于每一个操作1就将新建的点的权值归零

#include <cstdio>
#include <algorithm>
#include <iostream> 
#include <cstring>
using namespace std;
typedef long long ll;
const int M = 4e5 + 10;
const int N = 1e5 + 10; 
struct OP{
    int type,u,dx;
}op[M];
vector<int> G[N]; 
ll w[N];//初始权值
int dep[N],fa[N];//深度,父亲
int son[N],sz[N],id[N],dfn[N],tt;//重儿子,子树大小,i的dfn,dfn是i的编号,dfn
int top[N];//链头
//线段树部分
struct Node{
    int l,r;
    ll sum,lazy;
}tr[N<<2];

void pushup(int p){
    tr[p].sum = tr[p<<1].sum +tr[p<<1|1].sum;
}
void build(int p,int l,int r){
    tr[p].l = l,tr[p].r = r;
    if(l == r){
        tr[p].sum = w[id[l]];
        return;
    }
    int mid = l + r >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}
void pushdown(int p){
    ll lazy = tr[p].lazy;
    if(lazy){
        tr[p<<1].lazy += lazy;
        tr[p<<1|1].lazy += lazy;
        tr[p<<1].sum += (tr[p<<1].r - tr[p<<1].l + 1)*lazy;
        tr[p<<1|1].sum += (tr[p<<1|1].r - tr[p<<1|1].l + 1)*lazy;
        tr[p].lazy = 0;    
    }
}
void update(int p,int L,int R,ll dx){
    int l = tr[p].l,r = tr[p].r;
    if(l >= L&&r <= R){
        tr[p].lazy += dx;
        tr[p].sum += (r - l + 1)*dx;
        return;
    }
    pushdown(p);
    int mid = l + r >> 1;
    if(L <= mid) update(p<<1,L,R,dx);
    if(R > mid) update(p<<1|1,L,R,dx);
    pushup(p);
}
ll query(int p,int L,int R){
    int l = tr[p].l,r = tr[p].r;
    if(L <= l&&R >= r) return tr[p].sum;
    pushdown(p);
    int mid = l + r >> 1;
    ll ans = 0;
    if(L <= mid) ans += query(p<<1,L,R);
    if(R > mid) ans += query(p<<1|1,L,R);
    return ans;
}
//线段树部分 

void dfs1(int u){
    sz[u] = 1;
    for(int i = 0;i < G[u].size();i++){
        int v = G[u][i];
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]){
            son[u] = v;
        }
    }
} 
//x为当前重链的链头 
void dfs2(int u,int x){
    dfn[u] = ++tt;
    id[tt] = u;
    top[u] = x;
    if(!son[u]) return;
    dfs2(son[u],x);
    for(int i = 0;i < G[u].size();i++){
        int v = G[u][i];
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v,v);
    }
}
void updateTree(int p,ll dx){
    update(1,dfn[p],dfn[p] + sz[p] - 1,dx);
}
void updateOne(int p,ll dx){
    update(1,dfn[p],dfn[p],dx);
}
ll qPath(int u,int v){
    ll ans = 0;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[v]) swap(u,v);
        ans = (ans + query(1,dfn[top[u]],dfn[u]));
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u,v);
    ans = ans + query(1,dfn[v],dfn[u]);
    return ans;
}
int main(){
    int m;scanf("%d",&m);
    int idx = 0;
    int cnt = 0;//现在树的大小 - 1
    for(int i = 1,opt,u,dx;i <= m;i++){
        scanf("%d",&opt);
        if(opt == 1){
            scanf("%d",&u);
            u++;
            G[u].push_back(++cnt + 1);
            G[cnt + 1].push_back(u);
            op[++idx].type = 1;
            op[idx].u = cnt + 1;
        }else if(opt == 2){
            //存下修改
            op[++idx].type = 2;
            scanf("%d%d",&op[idx].u,&op[idx].dx); 
            op[idx].u++;
        }else{
            //存下询问
            op[++idx].type = 3;
            scanf("%d",&op[idx].u);
            op[idx].u++;
        }
    }
    int n = cnt + 1;
    dfs1(1);
    dfs2(1,-1);
    build(1,1,n);

    for(int i = 1;i <= idx;i++){
        if(op[i].type == 1){
            updateOne(op[i].u,-qPath(op[i].u,op[i].u));
        }else if(op[i].type == 2){
            updateTree(op[i].u,op[i].dx); 
        }else{
            printf("%lld\n",qPath(op[i].u,op[i].u));
        }
    }
    return 0;
}
每日一题 文章被收录于专栏

每题一题题目

全部评论

相关推荐

牛客279957775号:铁暗恋
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务