Luogu 3178树上操作

Posted ve-2021

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu 3178树上操作相关的知识,希望对你有一定的参考价值。

【原题题面】传送门

【思考过程】

树剖板子。

说好了这题写Dfs序。

 好我已经看出来了这题用树状数组不是很好搞。

opt1:单点修改

opt2:区间修改

opt3:“区间查询”

从根节点到x的路径上的点在dfs序上的表现是:时间戳便利到当前结点,只有in还没有out,即只进入对应的子树还没有返回到自身的结点,都在当前询问结点到根节点的路径上。

线段树维护的值有每个节点的权值,按照dfs序建线段树。

线段树记录进栈的节点数为cnt[1]++,已出栈的节点数为cnt[0]++;

每次更新节点时用栈内节点减去出栈节点(cnt[1]-cnt[0])来更新dat值

【调试过程】

dfs序两倍数组没开疯狂RE,气得我直接每个数组开long long,mxn*2...

【code】

技术图片
#include<bits/stdc++.h>
using namespace std;
#define File ""
#define ll long long
#define ull unsigned long long
inline void file(){
    freopen(File".in","r",stdin);
    freopen(File".out","w",stdout);
}
inline ll read(){
    ll x=0,f=1;   char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();}
    while(ch>=0&&ch<=9){x=(x<<1)+(x<<3)+ch-0; ch=getchar();}
    return x*f;
}
const int mxn = 1e5+5;
int n,m;
int w[mxn];

struct edge{
    int nxt,y;
}e[mxn<<1];
int to[mxn],len;
inline void add(int xx,int yy){
    e[++len].nxt = to[xx];
    to[xx] = len;
    e[len].y = yy;
}

bool v[mxn<<1];
int tot(0);
int a[mxn<<1],in[mxn<<1],ou[mxn<<1];
inline void dfs(int x,int fa){
    a[++tot] = x;
    in[x] = tot;
    v[tot] = 1;
    for(int i = to[x]; i;i = e[i].nxt){
        int y = e[i].y;
        if(y == fa) continue;
        dfs(y,x);
    }
    a[++tot] = x;
    ou[x] = tot;
    v[tot] = 0;
}

struct T{
    int l,r,cnt[2];
    ll tg,dat;
}tr[mxn<<4];
#define ls p<<1
#define rs p<<1|1
inline void U(int p){
    tr[p].dat = tr[ls].dat + tr[rs].dat;
    tr[p].cnt[0] = tr[ls].cnt[0] + tr[rs].cnt[0];
    tr[p].cnt[1] = tr[ls].cnt[1] + tr[rs].cnt[1];
}
inline void build(int p,int l,int r){
    tr[p].l = l,tr[p].r = r;
    tr[p].tg = 0;
    if(l == r){
        tr[p].cnt[v[l]] ++;
        tr[p].dat = v[l] ? w[a[l]] : -w[a[l]];
        return;
    }
    int mid = l+r >>1;
    build(ls,l,mid),build(rs,mid+1,r);
    U(p);
}
inline void push(int p){
    ll tg = tr[p].tg;
    if(tg){
        tr[ls].tg += tg;
        tr[rs].tg += tg;
        tr[ls].dat += tg*1ll*(tr[ls].cnt[1]-tr[ls].cnt[0]);
        tr[rs].dat += tg*1ll*(tr[rs].cnt[1]-tr[rs].cnt[0]);
        tr[p].tg = 0;
    }
}
inline void update1(int p,int l,int r,ll d){
    if(l <= tr[p].l && tr[p].r <= r){
        tr[p].tg += d;
        tr[p].dat += d*1ll*(tr[p].cnt[1]-tr[p].cnt[0]);
        return;
    }
    push(p);
    int mid = tr[p].l + tr[p].r >>1;
    if(l <= mid) update1(ls,l,r,d);
    if(r > mid) update1(rs,l,r,d);
    U(p);
}//区间修改
inline ll query(int p,int l,int r){
    ll ans(0);
    if(l <= tr[p].l && tr[p].r <= r) return tr[p].dat;
    push(p);
    int mid = tr[p].l + tr[p].r >>1;
    if(l <= mid) ans += query(ls,l,r);
    if(r > mid) ans += query(rs,l,r);
    return ans;
}

int main(){
//    file();
    scanf("%d %d",&n,&m);
    for(int i = 1;i <= n; ++i) scanf("%d",&w[i]);
    for(int i = 1;i < n; ++i){
        int x = read(),y = read();
        add(x,y),add(y,x);
    }
    memset(v,0,sizeof v);
    dfs(1,0);
    build(1,1,tot);
    for(int i = 1;i <= m; ++i){
        int opt;    scanf("%d",&opt);
        if(opt==1){
            int x;    scanf("%d",&x);
            int d = read();
            update1(1,in[x],in[x],d),update1(1,ou[x],ou[x],d);
        }else if(opt==2){
            int x = read(),d = read();
            update1(1,in[x],ou[x],d);
        }else{
            int x = read();
            printf("%lld\n",query(1,1,in[x]));
        }
    }
    return 0;
}
/*
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
*/
View Code

 

以上是关于Luogu 3178树上操作的主要内容,如果未能解决你的问题,请参考以下文章

「Luogu P3178」[HAOI2015]树上操作

Luogu 3178树上操作

Luogu P3178 树上操作(树链剖分+线段树)

luogu P3178 [HAOI2015]树上操作

P3178 树上操作

洛谷——P3178 [HAOI2015]树上操作