【BZOJ 4034】[HAOI2015]树上操作 差分+dfs序+树状数组

我们只要看出来这道题 数组表示的含义就是 某个点到根节点路径权值和就行 那么我们可以把最终答案 看做 k*x+b x就是其深度 ,我们发现dfs序之后,修改一个点是差分一个区间,修改一个点的子树,可以看做对于子树中的每一个点进行 a*(deep[x]-deep[root]+1) root是子树根节点,那么我们对 k 用a差分区间 对b用 -a*(deep[root]-1)差分区间

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define MAXN 100010
using namespace std;
typedef long long LL;
LL k[MAXN],b[MAXN];
int n,m;
inline LL sum_k(int x)
{
    LL sum=0;
    while(x>0) sum+=k[x],x-=x&(-x);   
    return sum;       
}
inline void ins_k(int x,LL key)
{
    while(x<=n) k[x]+=key,x+=x&(-x);
}
inline LL sum_b(int x)
{
    LL sum=0;
    while(x>0) sum+=b[x],x-=x&(-x);
    return sum;
}
inline void ins_b(int x,LL key)
{
    while(x<=n) b[x]+=key,x+=x&(-x);
} 
int l[MAXN],r[MAXN];
struct VIA
{
    int to,next;
}c[MAXN<<1];
int head[MAXN],t,Ti,deep[MAXN];
inline void add(int x,int y)
{
  c[++t].to=y;
  c[t].next=head[x];
  head[x]=t;
}
void dfs(int x)
{
   l[x]=++Ti;
   for(int i=head[x];i;i=c[i].next)
   if(l[c[i].to]==0)
   {
     deep[c[i].to]=deep[x]+1;
     dfs(c[i].to);
   }
   r[x]=Ti;
}
int temp[MAXN];
inline void Init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&temp[i]);
    for(int i=1;i<n;i++)
    {
       int x,y;
       scanf("%d%d",&x,&y);
       add(x,y);
       add(y,x); 
    }
    deep[1]=1;
    dfs(1);
    for(int i=1;i<=n;i++)
     ins_b(l[i],temp[i]),ins_b(r[i]+1,-temp[i]);
}
inline void work()
{
    while(m--)
    {
      int opt,x,a;
      scanf("%d%d",&opt,&x);
      if(opt==1)
      {
        scanf("%d",&a);
        ins_b(l[x],a),ins_b(r[x]+1,-a);
      }else
      if(opt==2)
      {
        scanf("%d",&a);
        ins_k(l[x],a),ins_k(r[x]+1,-a);
        ins_b(l[x],-(LL)a*(deep[x]-1)),ins_b(r[x]+1,(LL)a*(deep[x]-1));
      }else
      {
        LL ans=sum_k(l[x])*deep[x]+sum_b(l[x]);
        printf("%lld\n",ans);
      }
    }
}
int main()
{
    Init();
    work();
    return 0;
}

 

posted @ 2017-07-26 20:47  TS_Hugh  阅读(407)  评论(0编辑  收藏  举报