[HAOI2015]树上操作

树剖裸题。

可以用树状数组,优化成O(nlogn)

只要找到根的路径上的值的和,考虑一次操作会影响到哪些位置查询的值。

采用差分。其实是对dfn序的区间加。

dfs找到dfn,dfn2

两个树状数组t1,t2;

操作1:t1.add(dfn[x],z),t1.add(dfn2[x]+1,-z) 子树差分,query的时候,得到的是路径上的单点加的标记。

操作2:对于在y(x的一个祖先)做子树加z的操作,对x的贡献是:(dep[x]-dep[y]+1)*z = dep[x]*z-(dep[y]-1)*z

差分一下。t1.add(dfn[y],-(dep[y]-1)*z)     t1.add(dfn2[y]+1,(dep[y]-1)*z)     t2.add(dfn[y],z)  t2.add(dfn2[y]+1,-z)

这样,查询x的时候,就是t1.query(dfn[x])+dep[x]*t2.query(dfn[x]) ,t1包含了单点加的祖先的值,以及子树加部分的减去部分。t2上是一些标记。扫到一个,乘上dep[x],就把差分实现了。

 

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
typedef long long ll;
int dfn[N],dfn2[N],dep[N];
int a[2*N],tot;
ll w[N];
int n,m;
struct arraytree{    
    ll f[2*N];
    void upda(int x,ll c){for(;x<=2*n;x+=x&(-x))f[x]+=c;}
    ll query(int x){ll ret=0;for(;x;x-=x&(-x))ret+=f[x];return ret;}
}t1,t2;

struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
void dfs(int x,int fa,int d){
    dfn[x]=++tot;
    dep[x]=d;
    for(int i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x,d+1);
    }
    dfn2[x]=tot;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&w[i]);int x,y;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);add(x,y);add(y,x);
    }
    dfs(1,0,1);
    //for(int i=1;i<=2*n;i++)cout<<a[i]<<" ";cout<<endl;
    //for(int i=1;i<=n;i++) cout<<i<<" : "<<dfn[i]<<" "<<dfn2[i]<<endl;
    for(int i=1;i<=n;i++){
        t1.upda(dfn[i],w[i]);
        t1.upda(dfn2[i]+1,-w[i]);
    }
    //for(int i=1;i<=n;i++) cout<<i<<" : "<<t1.query(dfn[i])<<endl;
    ll z;int op;
    for(int i=1;i<=m;i++){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%lld",&x,&z);
            t1.upda(dfn[x],z);
            t1.upda(dfn2[x]+1,-z);
        }
        else if(op==2){
            scanf("%d%lld",&x,&z);
            t1.upda(dfn[x],-(dep[x]-1)*z);
            t1.upda(dfn2[x]+1,(dep[x]-1)*z);
            t2.upda(dfn[x],z);
            t2.upda(dfn2[x]+1,-z);
        }
        else{
            scanf("%d",&x);
            printf("%lld\n",t1.query(dfn[x])+dep[x]*t2.query(dfn[x]));
        }
    }
    return 0;
}

 

posted @ 2018-12-12 08:24  *Miracle*  阅读(145)  评论(0编辑  收藏  举报