LOJ147 dfs序4 题解(dfs序+树状数组+树上差分)

题目:LOJ147.
题目大意:给定一棵 n n n个点的树,要求支持以下操作:
1.格式 1   a   x 1\,a\,x 1ax,表示将点 a a a的点权增加 x x x.
2.格式 2   a   x 2\,a\,x 2ax,表示将点 a a a的子树增加 x x x.
3.格式 3   a   b 3\,a\,b 3ab,表示查询链 ( a , b ) (a,b) (a,b)的点权和.
设操作数量为 m m m,则 1 ≤ n , m ≤ 1 0 6 1\leq n,m\leq 10^6 1n,m106.

同时维护两个修改貌似并不好搞,所以考虑分成修改点权+查询链和修改子树+查询链两个子问题.

子问题1:修改点权+查询链可以通过维护每个点到根这条链的点权和来实现.此时修改点权想到与修改子树,查询链相当于查询点权,然后通过树上差分实现查询任意链的点权和.

子问题2:修改子树+查询链仍然通过维护每个点 x x x到根这条链的点权和 v [ x ] v[x] v[x].设点 x x x深度为 d e p [ k ] dep[k] dep[k],那么点 x x x子树中,那么对子树 x x x增加 w w w会使得 v [ y ] v[y] v[y]增加 w ( d e p [ y ] − d e p [ x ] + 1 ) = w ∗ d e p [ y ] − w ( d e p [ x ] − 1 ) w(dep[y]-dep[x]+1)=w*dep[y]-w(dep[x]-1) w(dep[y]dep[x]+1)=wdep[y]w(dep[x]1),发现 − w ( d e p [ x ] − 1 ) -w(dep[x]-1) w(dep[x]1)可以与子问题1一起维护, w ∗ d e p [ y ] w*dep[y] wdep[y]可以再维护一个树状数组来实现.

时间复杂度 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn).

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=1000000;

int Ri(){
  int x=0,y=1;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar()) if (c=='-') y=-1;
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x*y;
}

int n,m,v[N+9],rot;
struct side{
  int y,next;
}e[N*2+9];
int lin[N+9],top;

void Ins(int x,int y){
  e[++top].y=y;
  e[top].next=lin[x];
  lin[x]=top;
}

int ld[N+9],rd[N+9],ord[N+9],co;
struct node{
  int fa,son,top,dep,siz;
}d[N+9];

void Dfs1(int k,int fa){
  ord[++co]=k;ld[k]=co;
  d[k].fa=fa;
  d[k].dep=d[fa].dep+1;
  d[k].siz=1;
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^fa) {
      Dfs1(e[i].y,k);
      d[k].siz+=d[e[i].y].siz;
      if (d[d[k].son].siz<d[e[i].y].siz) d[k].son=e[i].y;
    }
  rd[k]=co;
}

void Dfs2(int k,int top){
  d[k].top=top;
  if (d[k].son) Dfs2(d[k].son,top);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].fa&&e[i].y^d[k].son) Dfs2(e[i].y,e[i].y);
}

int Lca(int x,int y){
  while (d[x].top^d[y].top)
    d[d[x].top].dep>d[d[y].top].dep?x=d[d[x].top].fa:y=d[d[y].top].fa;
  return d[x].dep>d[y].dep?y:x;
}

LL c[2][N+9];

void Bit_add(int t,int x,LL v){if (!x) return;for (;x<=n;x+=x&-x) c[t][x]+=v;}
LL Bit_query(int t,int x){LL sum=0;for (;x;x-=x&-x) sum+=c[t][x];return sum;}
LL Bit_query(int t,int l,int r){return Bit_query(t,r)-Bit_query(t,l-1);}
LL Query_chain(int x){return Bit_query(1,ld[x])*d[x].dep+Bit_query(0,ld[x]);}

LL Query_chain(int x,int y){
  int lca=Lca(x,y);
  return Query_chain(x)+Query_chain(y)-Query_chain(lca)-Query_chain(d[lca].fa);
}

void Add_node(int x,LL v){Bit_add(0,ld[x],v);Bit_add(0,rd[x]+1,-v);}
void Add_tree(int x,LL v){Bit_add(1,ld[x],v);Bit_add(1,rd[x]+1,-v);Add_node(x,-v*(d[x].dep-1));}
void Build(){for (int i=1;i<=n;++i) Add_node(i,(LL)v[i]);}

Abigail into(){
  int x,y;
  n=Ri();m=Ri();rot=Ri();
  for (int i=1;i<=n;++i)
    v[i]=Ri();
  for (int i=1;i<n;++i){
  	x=Ri();y=Ri();
  	Ins(x,y);Ins(y,x);
  }
}

Abigail work(){
  Dfs1(rot,0);
  Dfs2(rot,rot);
  Build();
}

Abigail getans(){
  int opt,a,x;
  while (m--){
  	opt=Ri();a=Ri();x=Ri();
  	switch (opt){
  	  case 1:
  	  	Add_node(a,(LL)x);
  	  	break;
  	  case 2:
  	  	Add_tree(a,(LL)x);
  	  	break;
  	  case 3:
  	  	printf("%lld\n",Query_chain(a,x));
  	  	break;
  	} 
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

你可能感兴趣的:(LOJ147 dfs序4 题解(dfs序+树状数组+树上差分))